···11+(lang dune 3.20)
22+(name cff)
33+(generate_opam_files true)
44+(license ISC)
55+(authors "The ocaml-cff programmers")
66+(maintainers "anil@recoil.org")
77+(source (github avsm/ocaml-cff))
88+99+(package
1010+ (name cff)
1111+ (synopsis "Citation File Format (CFF) codec for OCaml")
1212+ (description "A library for parsing and generating CITATION.cff files following the CFF 1.2.0 specification")
1313+ (depends
1414+ (ocaml (>= 4.14.0))
1515+ ptime
1616+ ISO3166
1717+ spdx_licenses
1818+ yamlt
1919+ jsont
2020+ bytesrw))
+69
project/ocaml-cff/lib/cff.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Citation File Format (CFF) codec for OCaml. *)
77+88+(* Module aliases *)
99+module Config = Cff_config
1010+module Date = Cff_date
1111+module Country = Cff_country
1212+module License = Cff_license
1313+1414+module Identifier_type = Cff_enums.Identifier_type
1515+module Reference_type = Cff_enums.Reference_type
1616+module Status = Cff_enums.Status
1717+module Cff_type = Cff_enums.Cff_type
1818+1919+module Address = Cff_address.Address
2020+module Contact = Cff_address.Contact
2121+2222+module Author = Cff_author
2323+module Name = Cff_author.Name
2424+module Person = Cff_author.Person
2525+module Entity = Cff_author.Entity
2626+2727+module Identifier = Cff_identifier
2828+module Reference = Cff_reference
2929+3030+(* Include the root type *)
3131+include Cff_root
3232+3333+(* YAML codec functions *)
3434+let of_yaml_string s =
3535+ let reader = Bytesrw.Bytes.Reader.of_string s in
3636+ match Yamlt.decode ~layout:true Cff_root.jsont reader with
3737+ | Ok cff -> Ok cff
3838+ | Error e -> Error e
3939+4040+let to_yaml_string t =
4141+ let buf = Buffer.create 1024 in
4242+ let writer = Bytesrw.Bytes.Writer.of_buffer buf in
4343+ match Yamlt.encode ~format:Yamlt.Block Cff_root.jsont t ~eod:true writer with
4444+ | Ok () -> Ok (Buffer.contents buf)
4545+ | Error e -> Error e
4646+4747+let of_yaml_file path =
4848+ try
4949+ let ic = open_in path in
5050+ let len = in_channel_length ic in
5151+ let s = really_input_string ic len in
5252+ close_in ic;
5353+ of_yaml_string s
5454+ with
5555+ | Sys_error e -> Error e
5656+ | e -> Error (Printexc.to_string e)
5757+5858+let to_yaml_file path t =
5959+ match to_yaml_string t with
6060+ | Error e -> Error e
6161+ | Ok s ->
6262+ try
6363+ let oc = open_out path in
6464+ output_string oc s;
6565+ close_out oc;
6666+ Ok ()
6767+ with
6868+ | Sys_error e -> Error e
6969+ | e -> Error (Printexc.to_string e)
+226
project/ocaml-cff/lib/cff.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Citation File Format (CFF) codec for OCaml.
77+88+ This library provides types and codecs for the
99+ {{:https://citation-file-format.github.io/}Citation File Format (CFF)}
1010+ version 1.2.0, a human- and machine-readable format for software and
1111+ dataset citation metadata.
1212+1313+ CFF files are plain text files named [CITATION.cff] written in
1414+ {{:https://yaml.org/}YAML 1.2}. They provide citation metadata for
1515+ software and datasets, enabling proper academic credit for research
1616+ software.
1717+1818+ {1 Overview}
1919+2020+ A minimal [CITATION.cff] file requires four fields:
2121+ - [cff-version]: The CFF schema version (currently ["1.2.0"])
2222+ - [message]: Instructions for citing the work
2323+ - [title]: The name of the software or dataset
2424+ - [authors]: A list of persons and/or entities
2525+2626+ {1 Quick Start}
2727+2828+ {2 Parsing an existing CITATION.cff}
2929+3030+ {[
3131+ match Cff.of_yaml_file "CITATION.cff" with
3232+ | Ok cff ->
3333+ Printf.printf "Title: %s\n" (Cff.title cff);
3434+ Printf.printf "Version: %s\n"
3535+ (Option.value ~default:"unspecified" (Cff.version cff))
3636+ | Error msg ->
3737+ Printf.eprintf "Parse error: %s\n" msg
3838+ ]}
3939+4040+ {2 Creating a CITATION.cff programmatically}
4141+4242+ {[
4343+ let author = Cff.Author.Person
4444+ (Cff.Person.make ~family_names:"Smith" ~given_names:"Jane" ()) in
4545+ let cff = Cff.make_simple
4646+ ~title:"My Research Software"
4747+ ~authors:[author]
4848+ ~version:"1.0.0"
4949+ ~doi:"10.5281/zenodo.1234567"
5050+ () in
5151+ match Cff.to_yaml_file "CITATION.cff" cff with
5252+ | Ok () -> print_endline "Created CITATION.cff"
5353+ | Error msg -> Printf.eprintf "Write error: %s\n" msg
5454+ ]}
5555+5656+ {1 Module Structure}
5757+5858+ The library uses a flat internal structure ([Cff_author], [Cff_date], etc.)
5959+ but exposes a convenient nested API through module aliases:
6060+6161+ - {!module:Author} - Person and entity types for authorship
6262+ - {!module:Reference} - Bibliographic reference with 60+ fields
6363+ - {!module:Identifier} - DOI, URL, SWH, and other identifiers
6464+ - {!module:License} - SPDX license identifiers
6565+ - {!module:Date} - ISO 8601 date handling
6666+6767+ {1 CFF Specification}
6868+6969+ This implementation follows the
7070+ {{:https://github.com/citation-file-format/citation-file-format}CFF 1.2.0 specification}.
7171+ Key concepts:
7272+7373+ - {b Authors}: Can be persons (with family/given names) or entities
7474+ (organizations, identified by a [name] field)
7575+ - {b References}: Bibliography entries that the work cites or depends on
7676+ - {b Preferred citation}: An alternate work to cite instead of the
7777+ software itself (e.g., a journal article about the software)
7878+ - {b Identifiers}: Typed identifiers including DOIs, URLs, and
7979+ Software Heritage IDs (SWH)
8080+ - {b Licenses}: SPDX license identifiers; multiple licenses imply OR
8181+8282+ {1 Core Types} *)
8383+8484+(** Configuration for validation strictness. *)
8585+module Config = Cff_config
8686+8787+(** Date representation as [(year, month, day)] tuple.
8888+8989+ CFF uses ISO 8601 dates in [YYYY-MM-DD] format (e.g., ["2024-01-15"]). *)
9090+module Date = Cff_date
9191+9292+(** ISO 3166-1 alpha-2 country codes (e.g., ["US"], ["DE"], ["GB"]).
9393+9494+ Used for author and entity addresses. *)
9595+module Country = Cff_country
9696+9797+(** SPDX license identifiers.
9898+9999+ CFF uses {{:https://spdx.org/licenses/}SPDX license identifiers} for
100100+ the [license] field. Multiple licenses indicate an OR relationship
101101+ (the user may choose any of the listed licenses). *)
102102+module License = Cff_license
103103+104104+(** {1 Enumeration Types} *)
105105+106106+(** Identifier types for the [identifiers] field.
107107+108108+ - [`Doi] - Digital Object Identifier
109109+ - [`Url] - Web URL
110110+ - [`Swh] - Software Heritage identifier
111111+ - [`Other] - Other identifier type *)
112112+module Identifier_type = Cff_enums.Identifier_type
113113+114114+(** Reference types for bibliographic entries.
115115+116116+ CFF supports 40+ reference types including [`Article], [`Book],
117117+ [`Software], [`Conference_paper], [`Thesis], [`Dataset], and more.
118118+ See {!Cff_enums.Reference_type} for the complete list. *)
119119+module Reference_type = Cff_enums.Reference_type
120120+121121+(** Publication status for works in progress.
122122+123123+ - [`Preprint] - Available as preprint
124124+ - [`Submitted] - Submitted for publication
125125+ - [`In_press] - Accepted, awaiting publication
126126+ - [`Advance_online] - Published online ahead of print *)
127127+module Status = Cff_enums.Status
128128+129129+(** CFF file type: [`Software] (default) or [`Dataset]. *)
130130+module Cff_type = Cff_enums.Cff_type
131131+132132+(** {1 Address and Contact Information} *)
133133+134134+(** Physical address with street, city, region, postal code, and country. *)
135135+module Address = Cff_address.Address
136136+137137+(** Contact information: email, telephone, fax, website, and ORCID. *)
138138+module Contact = Cff_address.Contact
139139+140140+(** {1 Authors and Entities} *)
141141+142142+(** Authors as a discriminated union of {!Person} or {!Entity}.
143143+144144+ CFF distinguishes between:
145145+ - {b Persons}: Individual humans with family names, given names, etc.
146146+ - {b Entities}: Organizations, projects, or groups with a [name] field
147147+148148+ When parsing, the presence of a [name] field indicates an entity;
149149+ otherwise, the entry is treated as a person. *)
150150+module Author = Cff_author
151151+152152+(** Person name components: family names, given names, particle, suffix, alias. *)
153153+module Name = Cff_author.Name
154154+155155+(** A person (individual author or contributor). *)
156156+module Person = Cff_author.Person
157157+158158+(** An entity (organization, institution, project, conference). *)
159159+module Entity = Cff_author.Entity
160160+161161+(** {1 Identifiers and References} *)
162162+163163+(** Typed identifiers for DOI, URL, SWH, or other schemes.
164164+165165+ Each identifier has a type, value, and optional description. Example:
166166+ {[
167167+ let id = Cff.Identifier.make
168168+ ~type_:`Doi
169169+ ~value:"10.5281/zenodo.1234567"
170170+ ~description:"The concept DOI for all versions"
171171+ ()
172172+ ]} *)
173173+module Identifier = Cff_identifier
174174+175175+(** Bibliographic references with comprehensive metadata.
176176+177177+ References can represent any citable work: articles, books, software,
178178+ datasets, conference papers, theses, etc. The {!Reference} module
179179+ provides 60+ fields organized into logical sub-records:
180180+181181+ - {!Reference.Core} - Type, title, authors, abstract
182182+ - {!Reference.Publication} - Journal, volume, issue, pages
183183+ - {!Reference.Collection} - Proceedings, book series
184184+ - {!Reference.Dates} - Various date fields and year
185185+ - {!Reference.Identifiers} - DOI, URL, ISBN, ISSN, etc.
186186+ - {!Reference.Entities} - Editors, publisher, institution
187187+ - {!Reference.Metadata} - Keywords, license, notes
188188+ - {!Reference.Technical} - Commit, version, format *)
189189+module Reference = Cff_reference
190190+191191+(** {1 Root CFF Type}
192192+193193+ The main [t] type represents a complete [CITATION.cff] file. It includes
194194+ the {!module:Cff_root} interface with all required and optional fields. *)
195195+196196+include module type of Cff_root
197197+198198+(** {1 YAML Codec}
199199+200200+ Parse and serialize CFF data using YAML format. The codec handles:
201201+ - YAML 1.2 syntax via the {{:https://erratique.ch/software/yamlt}Yamlt} library
202202+ - Flexible input (quoted/unquoted strings, integers as strings)
203203+ - SPDX license validation (with lenient mode for deprecated IDs)
204204+ - Person/entity discrimination based on [name] field presence *)
205205+206206+val of_yaml_string : string -> (t, string) result
207207+(** [of_yaml_string s] parses a CFF from YAML string [s].
208208+209209+ Returns [Ok cff] on success or [Error msg] with a descriptive error
210210+ message on failure. *)
211211+212212+val to_yaml_string : t -> (string, string) result
213213+(** [to_yaml_string cff] serializes [cff] to a YAML string.
214214+215215+ The output uses YAML block style for readability. *)
216216+217217+val of_yaml_file : string -> (t, string) result
218218+(** [of_yaml_file path] reads and parses a [CITATION.cff] file.
219219+220220+ Returns [Ok cff] on success or [Error msg] if the file cannot be
221221+ read or contains invalid CFF data. *)
222222+223223+val to_yaml_file : string -> t -> (unit, string) result
224224+(** [to_yaml_file path cff] writes [cff] to a file at [path].
225225+226226+ Creates or overwrites the file. Returns [Error msg] on I/O failure. *)
+91
project/ocaml-cff/lib/cff_address.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Address and contact information for CFF. *)
77+88+(** Physical address information. *)
99+module Address = struct
1010+ type t = {
1111+ address : string option;
1212+ city : string option;
1313+ region : string option;
1414+ post_code : string option;
1515+ country : string option; (* ISO 3166-1 alpha-2 *)
1616+ }
1717+1818+ let empty = {
1919+ address = None;
2020+ city = None;
2121+ region = None;
2222+ post_code = None;
2323+ country = None;
2424+ }
2525+2626+ let make ?address ?city ?region ?post_code ?country () =
2727+ { address; city; region; post_code; country }
2828+2929+ let address t = t.address
3030+ let city t = t.city
3131+ let region t = t.region
3232+ let post_code t = t.post_code
3333+ let country t = t.country
3434+3535+ let is_empty t =
3636+ t.address = None && t.city = None && t.region = None &&
3737+ t.post_code = None && t.country = None
3838+3939+ let pp ppf t =
4040+ let parts = List.filter_map Fun.id [
4141+ t.address;
4242+ t.city;
4343+ t.region;
4444+ t.post_code;
4545+ t.country;
4646+ ] in
4747+ Format.pp_print_string ppf (String.concat ", " parts)
4848+end
4949+5050+(** Contact information. *)
5151+module Contact = struct
5252+ type t = {
5353+ email : string option;
5454+ tel : string option;
5555+ fax : string option;
5656+ website : string option;
5757+ orcid : string option;
5858+ }
5959+6060+ let empty = {
6161+ email = None;
6262+ tel = None;
6363+ fax = None;
6464+ website = None;
6565+ orcid = None;
6666+ }
6767+6868+ let make ?email ?tel ?fax ?website ?orcid () =
6969+ { email; tel; fax; website; orcid }
7070+7171+ let email t = t.email
7272+ let tel t = t.tel
7373+ let fax t = t.fax
7474+ let website t = t.website
7575+ let orcid t = t.orcid
7676+7777+ let is_empty t =
7878+ t.email = None && t.tel = None && t.fax = None &&
7979+ t.website = None && t.orcid = None
8080+8181+ let pp ppf t =
8282+ let parts = List.filter_map (fun (k, v) ->
8383+ Option.map (fun v -> k ^ ": " ^ v) v
8484+ ) [
8585+ ("email", t.email);
8686+ ("tel", t.tel);
8787+ ("website", t.website);
8888+ ("orcid", t.orcid);
8989+ ] in
9090+ Format.pp_print_string ppf (String.concat ", " parts)
9191+end
+149
project/ocaml-cff/lib/cff_address.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Physical address and contact information for CFF.
77+88+ CFF includes address and contact fields for both persons and entities.
99+ This module provides types for these shared fields.
1010+1111+ {1 Address Fields}
1212+1313+ Physical address components appear on both persons and entities:
1414+1515+ - [address]: Street address (e.g., ["123 Main St"])
1616+ - [city]: City name (e.g., ["Cambridge"])
1717+ - [region]: State, province, or region (e.g., ["Massachusetts"])
1818+ - [post-code]: Postal/ZIP code (e.g., ["02139"])
1919+ - [country]: ISO 3166-1 alpha-2 country code (e.g., ["US"])
2020+2121+ {1 Contact Fields}
2222+2323+ Contact information available for persons and entities:
2424+2525+ - [email]: Email address
2626+ - [tel]: Telephone number
2727+ - [fax]: Fax number
2828+ - [website]: Website URL
2929+ - [orcid]: ORCID identifier URL (for researchers)
3030+3131+ {1 Example}
3232+3333+ {[
3434+ authors:
3535+ - family-names: Smith
3636+ given-names: Jane
3737+ affiliation: MIT
3838+ address: 77 Massachusetts Avenue
3939+ city: Cambridge
4040+ region: Massachusetts
4141+ post-code: "02139"
4242+ country: US
4343+ email: jsmith@mit.edu
4444+ orcid: https://orcid.org/0000-0001-2345-6789
4545+ ]} *)
4646+4747+(** Physical address information.
4848+4949+ All fields are optional; an empty address is valid. *)
5050+module Address : sig
5151+ type t
5252+ (** Physical address record. *)
5353+5454+ val empty : t
5555+ (** Empty address with all fields [None]. *)
5656+5757+ val make :
5858+ ?address:string ->
5959+ ?city:string ->
6060+ ?region:string ->
6161+ ?post_code:string ->
6262+ ?country:string ->
6363+ unit -> t
6464+ (** Create an address with optional fields.
6565+6666+ @param address Street address
6767+ @param city City name
6868+ @param region State, province, or administrative region
6969+ @param post_code Postal code, ZIP code, or postcode
7070+ @param country ISO 3166-1 alpha-2 country code *)
7171+7272+ val address : t -> string option
7373+ (** Street address (e.g., ["77 Massachusetts Avenue"]). *)
7474+7575+ val city : t -> string option
7676+ (** City name (e.g., ["Cambridge"], ["London"]). *)
7777+7878+ val region : t -> string option
7979+ (** State, province, or region (e.g., ["Massachusetts"], ["Bavaria"]). *)
8080+8181+ val post_code : t -> string option
8282+ (** Postal or ZIP code (e.g., ["02139"], ["W1A 1AA"]). *)
8383+8484+ val country : t -> string option
8585+ (** ISO 3166-1 alpha-2 country code (e.g., ["US"], ["DE"], ["GB"]).
8686+8787+ See {!Cff_country} for country code validation. *)
8888+8989+ val is_empty : t -> bool
9090+ (** [true] if all fields are [None]. *)
9191+9292+ val pp : Format.formatter -> t -> unit
9393+ (** Pretty-print the address. *)
9494+end
9595+9696+(** Contact information.
9797+9898+ Electronic contact details for persons and entities. All fields
9999+ are optional. *)
100100+module Contact : sig
101101+ type t
102102+ (** Contact information record. *)
103103+104104+ val empty : t
105105+ (** Empty contact with all fields [None]. *)
106106+107107+ val make :
108108+ ?email:string ->
109109+ ?tel:string ->
110110+ ?fax:string ->
111111+ ?website:string ->
112112+ ?orcid:string ->
113113+ unit -> t
114114+ (** Create contact information with optional fields.
115115+116116+ @param email Email address
117117+ @param tel Telephone number (any format)
118118+ @param fax Fax number (any format)
119119+ @param website Website URL
120120+ @param orcid ORCID identifier URL *)
121121+122122+ val email : t -> string option
123123+ (** Email address (e.g., ["jane.smith\@example.org"]). *)
124124+125125+ val tel : t -> string option
126126+ (** Telephone number. No specific format is required. *)
127127+128128+ val fax : t -> string option
129129+ (** Fax number. No specific format is required. *)
130130+131131+ val website : t -> string option
132132+ (** Website URL (e.g., ["https://example.org/~jsmith"]). *)
133133+134134+ val orcid : t -> string option
135135+ (** ORCID identifier as a URL.
136136+137137+ ORCID (Open Researcher and Contributor ID) provides persistent
138138+ digital identifiers for researchers.
139139+140140+ Format: ["https://orcid.org/XXXX-XXXX-XXXX-XXXX"]
141141+142142+ Example: ["https://orcid.org/0000-0001-2345-6789"] *)
143143+144144+ val is_empty : t -> bool
145145+ (** [true] if all fields are [None]. *)
146146+147147+ val pp : Format.formatter -> t -> unit
148148+ (** Pretty-print the contact information. *)
149149+end
+299
project/ocaml-cff/lib/cff_author.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Person, Entity, and Author types for CFF. *)
77+88+(** Person name components. *)
99+module Name = struct
1010+ type t = {
1111+ family_names : string option;
1212+ given_names : string option;
1313+ name_particle : string option; (* e.g., "von" *)
1414+ name_suffix : string option; (* e.g., "Jr." *)
1515+ alias : string option;
1616+ }
1717+1818+ let empty = {
1919+ family_names = None;
2020+ given_names = None;
2121+ name_particle = None;
2222+ name_suffix = None;
2323+ alias = None;
2424+ }
2525+2626+ let make ?family_names ?given_names ?name_particle ?name_suffix ?alias () =
2727+ { family_names; given_names; name_particle; name_suffix; alias }
2828+2929+ let family_names t = t.family_names
3030+ let given_names t = t.given_names
3131+ let name_particle t = t.name_particle
3232+ let name_suffix t = t.name_suffix
3333+ let alias t = t.alias
3434+3535+ let full_name t =
3636+ let parts = List.filter_map Fun.id [
3737+ t.given_names;
3838+ t.name_particle;
3939+ t.family_names;
4040+ ] in
4141+ let base = String.concat " " parts in
4242+ match t.name_suffix with
4343+ | Some suffix -> base ^ ", " ^ suffix
4444+ | None -> base
4545+4646+ let pp ppf t =
4747+ Format.pp_print_string ppf (full_name t)
4848+end
4949+5050+(** A person (individual author/contributor). *)
5151+module Person = struct
5252+ type t = {
5353+ name : Name.t;
5454+ affiliation : string option;
5555+ address : Cff_address.Address.t;
5656+ contact : Cff_address.Contact.t;
5757+ }
5858+5959+ let make
6060+ ?family_names ?given_names ?name_particle ?name_suffix ?alias
6161+ ?affiliation
6262+ ?address ?city ?region ?post_code ?country
6363+ ?email ?tel ?fax ?website ?orcid
6464+ () =
6565+ let name = Name.make ?family_names ?given_names ?name_particle
6666+ ?name_suffix ?alias () in
6767+ let address_rec = Cff_address.Address.make ?address ?city ?region
6868+ ?post_code ?country () in
6969+ let contact = Cff_address.Contact.make ?email ?tel ?fax ?website ?orcid () in
7070+ { name; affiliation; address = address_rec; contact }
7171+7272+ let name t = t.name
7373+ let affiliation t = t.affiliation
7474+ let address t = t.address
7575+ let contact t = t.contact
7676+7777+ let family_names t = Name.family_names t.name
7878+ let given_names t = Name.given_names t.name
7979+ let name_particle t = Name.name_particle t.name
8080+ let name_suffix t = Name.name_suffix t.name
8181+ let alias t = Name.alias t.name
8282+ let full_name t = Name.full_name t.name
8383+8484+ let email t = Cff_address.Contact.email t.contact
8585+ let orcid t = Cff_address.Contact.orcid t.contact
8686+ let website t = Cff_address.Contact.website t.contact
8787+8888+ let pp ppf t =
8989+ Format.fprintf ppf "%s" (full_name t);
9090+ Option.iter (Format.fprintf ppf " (%s)") t.affiliation
9191+9292+ let jsont =
9393+ Jsont.Object.map ~kind:"Person"
9494+ (fun family_names given_names name_particle name_suffix alias
9595+ affiliation address city region post_code country
9696+ email tel fax website orcid ->
9797+ let name = Name.make ?family_names ?given_names ?name_particle
9898+ ?name_suffix ?alias () in
9999+ let address_rec = Cff_address.Address.make ?address ?city ?region
100100+ ?post_code ?country () in
101101+ let contact = Cff_address.Contact.make ?email ?tel ?fax ?website ?orcid () in
102102+ { name; affiliation; address = address_rec; contact })
103103+ |> Jsont.Object.opt_mem "family-names" Jsont.string
104104+ ~enc:(fun p -> Name.family_names p.name)
105105+ |> Jsont.Object.opt_mem "given-names" Jsont.string
106106+ ~enc:(fun p -> Name.given_names p.name)
107107+ |> Jsont.Object.opt_mem "name-particle" Jsont.string
108108+ ~enc:(fun p -> Name.name_particle p.name)
109109+ |> Jsont.Object.opt_mem "name-suffix" Jsont.string
110110+ ~enc:(fun p -> Name.name_suffix p.name)
111111+ |> Jsont.Object.opt_mem "alias" Jsont.string
112112+ ~enc:(fun p -> Name.alias p.name)
113113+ |> Jsont.Object.opt_mem "affiliation" Jsont.string
114114+ ~enc:(fun p -> p.affiliation)
115115+ |> Jsont.Object.opt_mem "address" Jsont.string
116116+ ~enc:(fun p -> Cff_address.Address.address p.address)
117117+ |> Jsont.Object.opt_mem "city" Jsont.string
118118+ ~enc:(fun p -> Cff_address.Address.city p.address)
119119+ |> Jsont.Object.opt_mem "region" Jsont.string
120120+ ~enc:(fun p -> Cff_address.Address.region p.address)
121121+ |> Jsont.Object.opt_mem "post-code" Jsont.string
122122+ ~enc:(fun p -> Cff_address.Address.post_code p.address)
123123+ |> Jsont.Object.opt_mem "country" Jsont.string
124124+ ~enc:(fun p -> Cff_address.Address.country p.address)
125125+ |> Jsont.Object.opt_mem "email" Jsont.string
126126+ ~enc:(fun p -> Cff_address.Contact.email p.contact)
127127+ |> Jsont.Object.opt_mem "tel" Jsont.string
128128+ ~enc:(fun p -> Cff_address.Contact.tel p.contact)
129129+ |> Jsont.Object.opt_mem "fax" Jsont.string
130130+ ~enc:(fun p -> Cff_address.Contact.fax p.contact)
131131+ |> Jsont.Object.opt_mem "website" Jsont.string
132132+ ~enc:(fun p -> Cff_address.Contact.website p.contact)
133133+ |> Jsont.Object.opt_mem "orcid" Jsont.string
134134+ ~enc:(fun p -> Cff_address.Contact.orcid p.contact)
135135+ |> Jsont.Object.skip_unknown
136136+ |> Jsont.Object.finish
137137+end
138138+139139+(** Event dates for entities (e.g., conferences). *)
140140+module Event_dates = struct
141141+ type t = {
142142+ date_start : Cff_date.t option;
143143+ date_end : Cff_date.t option;
144144+ }
145145+146146+ let empty = { date_start = None; date_end = None }
147147+148148+ let make ?date_start ?date_end () = { date_start; date_end }
149149+150150+ let date_start t = t.date_start
151151+ let date_end t = t.date_end
152152+153153+ let is_empty t = t.date_start = None && t.date_end = None
154154+155155+ let pp ppf t =
156156+ match t.date_start, t.date_end with
157157+ | Some s, Some e ->
158158+ Format.fprintf ppf "%a - %a" Cff_date.pp s Cff_date.pp e
159159+ | Some s, None ->
160160+ Format.fprintf ppf "%a -" Cff_date.pp s
161161+ | None, Some e ->
162162+ Format.fprintf ppf "- %a" Cff_date.pp e
163163+ | None, None -> ()
164164+end
165165+166166+(** An entity (organization, team, conference, etc.). *)
167167+module Entity = struct
168168+ type t = {
169169+ name : string;
170170+ alias : string option;
171171+ address : Cff_address.Address.t;
172172+ contact : Cff_address.Contact.t;
173173+ event_dates : Event_dates.t;
174174+ location : string option;
175175+ }
176176+177177+ let make
178178+ ~name ?alias
179179+ ?address ?city ?region ?post_code ?country
180180+ ?email ?tel ?fax ?website ?orcid
181181+ ?date_start ?date_end ?location
182182+ () =
183183+ let address_rec = Cff_address.Address.make ?address ?city ?region
184184+ ?post_code ?country () in
185185+ let contact = Cff_address.Contact.make ?email ?tel ?fax ?website ?orcid () in
186186+ let event_dates = Event_dates.make ?date_start ?date_end () in
187187+ { name; alias; address = address_rec; contact; event_dates; location }
188188+189189+ let name t = t.name
190190+ let alias t = t.alias
191191+ let address t = t.address
192192+ let contact t = t.contact
193193+ let event_dates t = t.event_dates
194194+ let location t = t.location
195195+196196+ let email t = Cff_address.Contact.email t.contact
197197+ let orcid t = Cff_address.Contact.orcid t.contact
198198+ let website t = Cff_address.Contact.website t.contact
199199+200200+ let pp ppf t =
201201+ Format.pp_print_string ppf t.name;
202202+ Option.iter (Format.fprintf ppf " (%s)") t.alias
203203+204204+ let jsont =
205205+ Jsont.Object.map ~kind:"Entity"
206206+ (fun name alias address city region post_code country
207207+ email tel fax website orcid date_start date_end location ->
208208+ let address_rec = Cff_address.Address.make ?address ?city ?region
209209+ ?post_code ?country () in
210210+ let contact = Cff_address.Contact.make ?email ?tel ?fax ?website ?orcid () in
211211+ let event_dates = Event_dates.make ?date_start ?date_end () in
212212+ { name; alias; address = address_rec; contact; event_dates; location })
213213+ |> Jsont.Object.mem "name" Jsont.string
214214+ ~enc:(fun e -> e.name)
215215+ |> Jsont.Object.opt_mem "alias" Jsont.string
216216+ ~enc:(fun e -> e.alias)
217217+ |> Jsont.Object.opt_mem "address" Jsont.string
218218+ ~enc:(fun e -> Cff_address.Address.address e.address)
219219+ |> Jsont.Object.opt_mem "city" Jsont.string
220220+ ~enc:(fun e -> Cff_address.Address.city e.address)
221221+ |> Jsont.Object.opt_mem "region" Jsont.string
222222+ ~enc:(fun e -> Cff_address.Address.region e.address)
223223+ |> Jsont.Object.opt_mem "post-code" Jsont.string
224224+ ~enc:(fun e -> Cff_address.Address.post_code e.address)
225225+ |> Jsont.Object.opt_mem "country" Jsont.string
226226+ ~enc:(fun e -> Cff_address.Address.country e.address)
227227+ |> Jsont.Object.opt_mem "email" Jsont.string
228228+ ~enc:(fun e -> Cff_address.Contact.email e.contact)
229229+ |> Jsont.Object.opt_mem "tel" Jsont.string
230230+ ~enc:(fun e -> Cff_address.Contact.tel e.contact)
231231+ |> Jsont.Object.opt_mem "fax" Jsont.string
232232+ ~enc:(fun e -> Cff_address.Contact.fax e.contact)
233233+ |> Jsont.Object.opt_mem "website" Jsont.string
234234+ ~enc:(fun e -> Cff_address.Contact.website e.contact)
235235+ |> Jsont.Object.opt_mem "orcid" Jsont.string
236236+ ~enc:(fun e -> Cff_address.Contact.orcid e.contact)
237237+ |> Jsont.Object.opt_mem "date-start" Cff_date.jsont
238238+ ~enc:(fun e -> Event_dates.date_start e.event_dates)
239239+ |> Jsont.Object.opt_mem "date-end" Cff_date.jsont
240240+ ~enc:(fun e -> Event_dates.date_end e.event_dates)
241241+ |> Jsont.Object.opt_mem "location" Jsont.string
242242+ ~enc:(fun e -> e.location)
243243+ |> Jsont.Object.skip_unknown
244244+ |> Jsont.Object.finish
245245+end
246246+247247+(** An author can be either a Person or an Entity. *)
248248+type t =
249249+ | Person of Person.t
250250+ | Entity of Entity.t
251251+252252+let person p = Person p
253253+let entity e = Entity e
254254+255255+let name = function
256256+ | Person p -> Person.full_name p
257257+ | Entity e -> Entity.name e
258258+259259+let orcid = function
260260+ | Person p -> Person.orcid p
261261+ | Entity e -> Entity.orcid e
262262+263263+let email = function
264264+ | Person p -> Person.email p
265265+ | Entity e -> Entity.email e
266266+267267+let pp ppf = function
268268+ | Person p -> Person.pp ppf p
269269+ | Entity e -> Entity.pp ppf e
270270+271271+(* Jsont codec that discriminates based on "name" field presence.
272272+ If "name" is present -> Entity, otherwise -> Person *)
273273+let jsont =
274274+ (* Check if json object has "name" member *)
275275+ let has_name_member = function
276276+ | Jsont.Object (members, _) -> Option.is_some (Jsont.Json.find_mem "name" members)
277277+ | _ -> false
278278+ in
279279+ let dec_json j =
280280+ if has_name_member j then
281281+ match Jsont.Json.decode' Entity.jsont j with
282282+ | Ok e -> Entity e
283283+ | Error err -> Jsont.Error.msgf Jsont.Meta.none "Invalid entity: %s" (Jsont.Error.to_string err)
284284+ else
285285+ match Jsont.Json.decode' Person.jsont j with
286286+ | Ok p -> Person p
287287+ | Error err -> Jsont.Error.msgf Jsont.Meta.none "Invalid person: %s" (Jsont.Error.to_string err)
288288+ in
289289+ let enc_author = function
290290+ | Person p ->
291291+ (match Jsont.Json.encode' Person.jsont p with
292292+ | Ok j -> j
293293+ | Error _ -> assert false)
294294+ | Entity e ->
295295+ (match Jsont.Json.encode' Entity.jsont e with
296296+ | Ok j -> j
297297+ | Error _ -> assert false)
298298+ in
299299+ Jsont.json |> Jsont.map ~dec:dec_json ~enc:enc_author
+405
project/ocaml-cff/lib/cff_author.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Authors for CFF: persons and entities.
77+88+ CFF distinguishes between two types of authors:
99+1010+ - {b Persons}: Individual humans identified by name components
1111+ (family names, given names, etc.)
1212+ - {b Entities}: Organizations, institutions, teams, projects, or
1313+ conferences identified by a single [name] field
1414+1515+ When parsing YAML, the library discriminates based on the presence
1616+ of a [name] field: if present, the entry is an entity; otherwise,
1717+ it's a person.
1818+1919+ {1 Name Components}
2020+2121+ CFF follows academic citation conventions for person names:
2222+2323+ - {b family-names}: Last name/surname (e.g., ["Smith"], ["van Rossum"])
2424+ - {b given-names}: First name(s) (e.g., ["Jane"], ["Guido"])
2525+ - {b name-particle}: Connector before family name (e.g., ["von"], ["van"], ["de"])
2626+ - {b name-suffix}: Generational suffix (e.g., ["Jr."], ["III"])
2727+ - {b alias}: Nickname or pseudonym
2828+2929+ {1 Entity Types}
3030+3131+ Entities can represent various organizations:
3232+3333+ - Research institutions and universities
3434+ - Companies and corporations
3535+ - Government agencies
3636+ - Open source projects and communities
3737+ - Academic conferences (with date-start/date-end)
3838+ - Standards bodies
3939+4040+ {1 Example}
4141+4242+ {[
4343+ (* A person author *)
4444+ let jane = Cff_author.Person (Cff_author.Person.make
4545+ ~family_names:"Smith"
4646+ ~given_names:"Jane A."
4747+ ~affiliation:"MIT"
4848+ ~orcid:"https://orcid.org/0000-0001-2345-6789"
4949+ ())
5050+5151+ (* A person with name particle *)
5252+ let guido = Cff_author.Person (Cff_author.Person.make
5353+ ~family_names:"Rossum"
5454+ ~given_names:"Guido"
5555+ ~name_particle:"van"
5656+ ())
5757+5858+ (* An organization entity *)
5959+ let mozilla = Cff_author.Entity (Cff_author.Entity.make
6060+ ~name:"Mozilla Foundation"
6161+ ~website:"https://mozilla.org"
6262+ ~city:"San Francisco"
6363+ ~country:"US"
6464+ ())
6565+6666+ (* A conference entity with dates *)
6767+ let conf = Cff_author.Entity (Cff_author.Entity.make
6868+ ~name:"ICSE 2024"
6969+ ~date_start:(2024, 4, 14)
7070+ ~date_end:(2024, 4, 20)
7171+ ~location:"Lisbon, Portugal"
7272+ ())
7373+ ]}
7474+7575+ {1 Name Components} *)
7676+7777+(** Name components for persons.
7878+7979+ CFF name handling follows scholarly citation conventions to properly
8080+ represent names from various cultures and naming traditions. *)
8181+module Name : sig
8282+ type t
8383+8484+ val empty : t
8585+ (** Empty name with all components as [None]. *)
8686+8787+ val make :
8888+ ?family_names:string ->
8989+ ?given_names:string ->
9090+ ?name_particle:string ->
9191+ ?name_suffix:string ->
9292+ ?alias:string ->
9393+ unit -> t
9494+ (** Create a name with optional components.
9595+9696+ @param family_names Last name/surname
9797+ @param given_names First name(s)
9898+ @param name_particle Connector like ["von"], ["van"], ["de"]
9999+ @param name_suffix Generational suffix like ["Jr."], ["III"]
100100+ @param alias Nickname or pseudonym *)
101101+102102+ val family_names : t -> string option
103103+ (** The person's family name (surname, last name). *)
104104+105105+ val given_names : t -> string option
106106+ (** The person's given name(s) (first name, forenames). *)
107107+108108+ val name_particle : t -> string option
109109+ (** Name connector appearing before family name.
110110+111111+ Examples: ["von"] in "Ludwig von Beethoven",
112112+ ["van"] in "Vincent van Gogh". *)
113113+114114+ val name_suffix : t -> string option
115115+ (** Generational or honorary suffix.
116116+117117+ Examples: ["Jr."], ["Sr."], ["III"], ["PhD"]. *)
118118+119119+ val alias : t -> string option
120120+ (** Nickname, pseudonym, or alternative name.
121121+122122+ Example: ["Tim"] for "Timothy", ["DHH"] for "David Heinemeier Hansson". *)
123123+124124+ val full_name : t -> string
125125+ (** Format name as "Given Particle Family, Suffix".
126126+127127+ Examples:
128128+ - ["Jane Smith"]
129129+ - ["Guido van Rossum"]
130130+ - ["John Smith, Jr."] *)
131131+132132+ val pp : Format.formatter -> t -> unit
133133+ (** Pretty-print the full name. *)
134134+end
135135+136136+(** Individual person (author, contributor, editor, etc.).
137137+138138+ A person represents a human contributor with:
139139+ - Name components (required: at least family or given names)
140140+ - Optional affiliation (institution, company)
141141+ - Optional physical address
142142+ - Optional contact information (email, ORCID, website) *)
143143+module Person : sig
144144+ type t
145145+146146+ val make :
147147+ ?family_names:string ->
148148+ ?given_names:string ->
149149+ ?name_particle:string ->
150150+ ?name_suffix:string ->
151151+ ?alias:string ->
152152+ ?affiliation:string ->
153153+ ?address:string ->
154154+ ?city:string ->
155155+ ?region:string ->
156156+ ?post_code:string ->
157157+ ?country:string ->
158158+ ?email:string ->
159159+ ?tel:string ->
160160+ ?fax:string ->
161161+ ?website:string ->
162162+ ?orcid:string ->
163163+ unit -> t
164164+ (** Create a person with optional fields.
165165+166166+ At minimum, provide [family_names] or [given_names].
167167+168168+ @param family_names Last name/surname
169169+ @param given_names First name(s)
170170+ @param name_particle Connector before family name
171171+ @param name_suffix Generational suffix
172172+ @param alias Nickname or pseudonym
173173+ @param affiliation Institution or organization name
174174+ @param address Street address
175175+ @param city City name
176176+ @param region State, province, or region
177177+ @param post_code Postal/ZIP code
178178+ @param country ISO 3166-1 alpha-2 country code
179179+ @param email Email address
180180+ @param tel Telephone number
181181+ @param fax Fax number
182182+ @param website Personal or professional website URL
183183+ @param orcid ORCID identifier URL (e.g., ["https://orcid.org/0000-0001-..."]) *)
184184+185185+ val name : t -> Name.t
186186+ (** The person's name components. *)
187187+188188+ val affiliation : t -> string option
189189+ (** The person's institutional affiliation.
190190+191191+ Example: ["Massachusetts Institute of Technology"]. *)
192192+193193+ val address : t -> Cff_address.Address.t
194194+ (** Physical address information. *)
195195+196196+ val contact : t -> Cff_address.Contact.t
197197+ (** Contact information (email, phone, web, ORCID). *)
198198+199199+ (** {2 Convenience Accessors for Name} *)
200200+201201+ val family_names : t -> string option
202202+ (** Shortcut for [Name.family_names (name t)]. *)
203203+204204+ val given_names : t -> string option
205205+ (** Shortcut for [Name.given_names (name t)]. *)
206206+207207+ val name_particle : t -> string option
208208+ (** Shortcut for [Name.name_particle (name t)]. *)
209209+210210+ val name_suffix : t -> string option
211211+ (** Shortcut for [Name.name_suffix (name t)]. *)
212212+213213+ val alias : t -> string option
214214+ (** Shortcut for [Name.alias (name t)]. *)
215215+216216+ val full_name : t -> string
217217+ (** Shortcut for [Name.full_name (name t)]. *)
218218+219219+ (** {2 Convenience Accessors for Contact} *)
220220+221221+ val email : t -> string option
222222+ (** The person's email address. *)
223223+224224+ val orcid : t -> string option
225225+ (** The person's ORCID identifier URL.
226226+227227+ ORCID (Open Researcher and Contributor ID) provides persistent
228228+ digital identifiers for researchers. Format: ["https://orcid.org/XXXX-XXXX-XXXX-XXXX"]. *)
229229+230230+ val website : t -> string option
231231+ (** The person's website URL. *)
232232+233233+ val pp : Format.formatter -> t -> unit
234234+ (** Pretty-print as "Full Name (affiliation)". *)
235235+236236+ val jsont : t Jsont.t
237237+ (** JSON/YAML codec for person records. *)
238238+end
239239+240240+(** Event date range for entities like conferences.
241241+242242+ Some entities (particularly conferences) have associated dates
243243+ when they take place. *)
244244+module Event_dates : sig
245245+ type t
246246+247247+ val empty : t
248248+ (** Empty date range with both dates as [None]. *)
249249+250250+ val make :
251251+ ?date_start:Cff_date.t ->
252252+ ?date_end:Cff_date.t ->
253253+ unit -> t
254254+ (** Create an event date range.
255255+256256+ @param date_start When the event begins
257257+ @param date_end When the event ends *)
258258+259259+ val date_start : t -> Cff_date.t option
260260+ (** The start date of the event. *)
261261+262262+ val date_end : t -> Cff_date.t option
263263+ (** The end date of the event. *)
264264+265265+ val is_empty : t -> bool
266266+ (** [true] if both dates are [None]. *)
267267+268268+ val pp : Format.formatter -> t -> unit
269269+ (** Pretty-print as "YYYY-MM-DD - YYYY-MM-DD". *)
270270+end
271271+272272+(** Organization, institution, project, or conference.
273273+274274+ An entity represents a non-person author or contributor, such as:
275275+ - Research institutions (["MIT"], ["CERN"])
276276+ - Companies (["Google"], ["Mozilla Foundation"])
277277+ - Government agencies (["NASA"], ["NIH"])
278278+ - Open source projects (["The Rust Project"])
279279+ - Academic conferences (["ICSE 2024"])
280280+ - Standards bodies (["IEEE"], ["W3C"])
281281+282282+ Entities are distinguished from persons in YAML by the presence
283283+ of a required [name] field (persons have [family-names]/[given-names]
284284+ instead). *)
285285+module Entity : sig
286286+ type t
287287+288288+ val make :
289289+ name:string ->
290290+ ?alias:string ->
291291+ ?address:string ->
292292+ ?city:string ->
293293+ ?region:string ->
294294+ ?post_code:string ->
295295+ ?country:string ->
296296+ ?email:string ->
297297+ ?tel:string ->
298298+ ?fax:string ->
299299+ ?website:string ->
300300+ ?orcid:string ->
301301+ ?date_start:Cff_date.t ->
302302+ ?date_end:Cff_date.t ->
303303+ ?location:string ->
304304+ unit -> t
305305+ (** Create an entity.
306306+307307+ @param name The entity's official name (required)
308308+ @param alias Short name or acronym
309309+ @param address Street address
310310+ @param city City name
311311+ @param region State, province, or region
312312+ @param post_code Postal/ZIP code
313313+ @param country ISO 3166-1 alpha-2 country code
314314+ @param email Contact email address
315315+ @param tel Telephone number
316316+ @param fax Fax number
317317+ @param website Official website URL
318318+ @param orcid Organization ORCID (rare but valid)
319319+ @param date_start Event start date (for conferences)
320320+ @param date_end Event end date (for conferences)
321321+ @param location Event location description *)
322322+323323+ val name : t -> string
324324+ (** The entity's official name. This field distinguishes entities
325325+ from persons in the YAML format. *)
326326+327327+ val alias : t -> string option
328328+ (** Short name, acronym, or alternative name.
329329+330330+ Example: ["MIT"] for "Massachusetts Institute of Technology". *)
331331+332332+ val address : t -> Cff_address.Address.t
333333+ (** Physical address information. *)
334334+335335+ val contact : t -> Cff_address.Contact.t
336336+ (** Contact information. *)
337337+338338+ val event_dates : t -> Event_dates.t
339339+ (** Event dates (for conferences). *)
340340+341341+ val location : t -> string option
342342+ (** Event location description (for conferences).
343343+344344+ Example: ["Lisbon, Portugal"]. *)
345345+346346+ (** {2 Convenience Accessors for Contact} *)
347347+348348+ val email : t -> string option
349349+ (** The entity's contact email. *)
350350+351351+ val orcid : t -> string option
352352+ (** The entity's ORCID (organizations can have ORCIDs). *)
353353+354354+ val website : t -> string option
355355+ (** The entity's official website URL. *)
356356+357357+ val pp : Format.formatter -> t -> unit
358358+ (** Pretty-print as "Name (alias)". *)
359359+360360+ val jsont : t Jsont.t
361361+ (** JSON/YAML codec for entity records. *)
362362+end
363363+364364+(** {1 Author Discriminated Union}
365365+366366+ The main author type is a sum type that can hold either a person
367367+ or an entity. This matches the CFF specification where authors
368368+ can be either individuals or organizations. *)
369369+370370+type t =
371371+ | Person of Person.t (** An individual person *)
372372+ | Entity of Entity.t (** An organization or entity *)
373373+(** An author: either a person or an entity. *)
374374+375375+val person : Person.t -> t
376376+(** Wrap a person as an author. *)
377377+378378+val entity : Entity.t -> t
379379+(** Wrap an entity as an author. *)
380380+381381+val name : t -> string
382382+(** Get the display name.
383383+384384+ For persons, returns the full formatted name.
385385+ For entities, returns the entity name. *)
386386+387387+val orcid : t -> string option
388388+(** Get the ORCID if present. Works for both persons and entities. *)
389389+390390+val email : t -> string option
391391+(** Get the email if present. Works for both persons and entities. *)
392392+393393+val pp : Format.formatter -> t -> unit
394394+(** Pretty-print the author. *)
395395+396396+val jsont : t Jsont.t
397397+(** JSON/YAML codec that discriminates based on [name] field presence.
398398+399399+ When decoding:
400400+ - If the object has a [name] field -> Entity
401401+ - Otherwise -> Person
402402+403403+ This matches the CFF specification where entities are distinguished
404404+ by having a [name] field while persons have [family-names] and
405405+ [given-names] fields. *)
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Configuration for CFF parsing and validation.
77+88+ CFF files in the wild may contain non-standard or deprecated values.
99+ This module provides configuration options to control validation
1010+ strictness during parsing.
1111+1212+ {1 Validation Modes}
1313+1414+ {2 Strict Mode}
1515+1616+ Validates all fields according to their specifications:
1717+1818+ - URLs must be well-formed
1919+ - Dates must be valid ISO 8601 dates
2020+ - DOIs must match the DOI pattern
2121+ - ORCIDs must be valid ORCID URLs
2222+ - License IDs must be valid SPDX identifiers
2323+2424+ Use strict mode for validating CFF files or when you control the input.
2525+2626+ {2 Lenient Mode}
2727+2828+ Accepts any string value without validation. Use lenient mode when:
2929+3030+ - Parsing CFF files from unknown sources
3131+ - Handling legacy files with deprecated license IDs
3232+ - Round-tripping files without data loss
3333+3434+ {2 Default Mode}
3535+3636+ A balanced approach that:
3737+ - Keeps unknown fields (for round-tripping)
3838+ - Uses lenient validation for most fields
3939+4040+ {1 Unknown Fields}
4141+4242+ The [keep_unknown] option controls handling of unrecognized fields:
4343+4444+ - [true]: Preserve unknown fields in the parsed structure
4545+ - [false]: Silently ignore unknown fields
4646+4747+ Keeping unknown fields allows round-tripping CFF files that contain
4848+ extensions or newer fields not yet supported by this library. *)
4949+5050+type t
5151+(** Configuration type. *)
5252+5353+val default : t
5454+(** Default configuration.
5555+5656+ Uses lenient validation and keeps unknown fields. Suitable for
5757+ general parsing where round-tripping is desired. *)
5858+5959+val strict : t
6060+(** Strict configuration.
6161+6262+ Validates all fields according to CFF 1.2.0 specification.
6363+ Fails on invalid URLs, dates, DOIs, ORCIDs, and license IDs.
6464+6565+ Keeps unknown fields for compatibility. *)
6666+6767+val lenient : t
6868+(** Fully lenient configuration.
6969+7070+ Accepts any string values without validation. Useful for parsing
7171+ malformed or non-standard CFF files. *)
7272+7373+val make :
7474+ ?strict_urls:bool ->
7575+ ?strict_dates:bool ->
7676+ ?strict_dois:bool ->
7777+ ?strict_orcids:bool ->
7878+ ?strict_licenses:bool ->
7979+ ?keep_unknown:bool ->
8080+ unit -> t
8181+(** Create a custom configuration.
8282+8383+ All strictness options default to [false] (lenient).
8484+ [keep_unknown] defaults to [true].
8585+8686+ @param strict_urls Validate URL format
8787+ @param strict_dates Validate date format and values
8888+ @param strict_dois Validate DOI pattern
8989+ @param strict_orcids Validate ORCID format
9090+ @param strict_licenses Validate SPDX license identifiers
9191+ @param keep_unknown Preserve unrecognized fields *)
9292+9393+val strict_urls : t -> bool
9494+(** Whether URL fields are validated. *)
9595+9696+val strict_dates : t -> bool
9797+(** Whether date fields are validated. *)
9898+9999+val strict_dois : t -> bool
100100+(** Whether DOI fields are validated. *)
101101+102102+val strict_orcids : t -> bool
103103+(** Whether ORCID fields are validated. *)
104104+105105+val strict_licenses : t -> bool
106106+(** Whether license identifiers are validated against SPDX. *)
107107+108108+val keep_unknown : t -> bool
109109+(** Whether unknown fields are preserved in the parsed structure. *)
+51
project/ocaml-cff/lib/cff_country.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Country code handling for CFF using ISO3166. *)
77+88+type t = string
99+1010+let of_string s =
1111+ (* Validate against ISO3166 alpha-2 codes *)
1212+ let s = String.uppercase_ascii s in
1313+ try
1414+ let _ = ISO3166.alpha2_of_string s in
1515+ Ok s
1616+ with Invalid_argument _ ->
1717+ Error (`Invalid_country s)
1818+1919+let to_string t = t
2020+2121+let to_iso3166 t =
2222+ try
2323+ Some (ISO3166.alpha2_to_country (ISO3166.alpha2_of_string t))
2424+ with Invalid_argument _ ->
2525+ None
2626+2727+let name t =
2828+ match to_iso3166 t with
2929+ | Some country -> Some (ISO3166.Country.name country)
3030+ | None -> None
3131+3232+let equal = String.equal
3333+let compare = String.compare
3434+3535+let pp ppf t =
3636+ Format.pp_print_string ppf t
3737+3838+(* Jsont codec for country codes *)
3939+let jsont =
4040+ let dec s =
4141+ match of_string s with
4242+ | Ok c -> c
4343+ | Error (`Invalid_country s) ->
4444+ Jsont.Error.msgf Jsont.Meta.none "Invalid ISO 3166-1 alpha-2 country code: %s" s
4545+ in
4646+ let enc t = to_string t in
4747+ Jsont.string
4848+ |> Jsont.map ~dec ~enc
4949+5050+(* Lenient codec that accepts any string *)
5151+let jsont_lenient = Jsont.string
+85
project/ocaml-cff/lib/cff_country.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** ISO 3166-1 alpha-2 country codes for CFF.
77+88+ CFF uses {{:https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2}
99+ ISO 3166-1 alpha-2} two-letter country codes for the [country]
1010+ field on persons and entities.
1111+1212+ {1 Format}
1313+1414+ Country codes are exactly two uppercase letters:
1515+1616+ - ["US"] - United States
1717+ - ["GB"] - United Kingdom
1818+ - ["DE"] - Germany
1919+ - ["FR"] - France
2020+ - ["JP"] - Japan
2121+ - ["CN"] - China
2222+ - ["AU"] - Australia
2323+ - ["CA"] - Canada
2424+ - ["CH"] - Switzerland
2525+ - ["NL"] - Netherlands
2626+2727+ {1 Validation}
2828+2929+ This module validates country codes against the {!ISO3166} library,
3030+ which maintains the official list of assigned codes.
3131+3232+ {1 Example}
3333+3434+ {[
3535+ authors:
3636+ - family-names: Müller
3737+ given-names: Hans
3838+ city: Berlin
3939+ country: DE
4040+ ]} *)
4141+4242+type t = string
4343+(** An ISO 3166-1 alpha-2 country code (two uppercase letters). *)
4444+4545+val of_string : string -> (t, [> `Invalid_country of string]) result
4646+(** Parse and validate a country code.
4747+4848+ Case-insensitive: ["us"], ["US"], and ["Us"] all produce ["US"].
4949+ Returns [Error (`Invalid_country s)] for unknown codes. *)
5050+5151+val to_string : t -> string
5252+(** Return the uppercase country code. *)
5353+5454+val to_iso3166 : t -> ISO3166.Country.t option
5555+(** Look up the full country record from {!ISO3166}.
5656+5757+ Returns [None] if the code is not in the ISO 3166-1 list. *)
5858+5959+val name : t -> string option
6060+(** Get the country name if the code is valid.
6161+6262+ Examples:
6363+ - [name "US" = Some "United States of America"]
6464+ - [name "GB" = Some "United Kingdom of Great Britain and Northern Ireland"]
6565+ - [name "XX" = None] *)
6666+6767+val equal : t -> t -> bool
6868+(** Country code equality (case-sensitive after normalization). *)
6969+7070+val compare : t -> t -> int
7171+(** Alphabetical comparison of country codes. *)
7272+7373+val pp : Format.formatter -> t -> unit
7474+(** Pretty-print the country code. *)
7575+7676+val jsont : t Jsont.t
7777+(** JSON/YAML codec that validates country codes.
7878+7979+ Returns an error for invalid ISO 3166-1 alpha-2 codes. *)
8080+8181+val jsont_lenient : t Jsont.t
8282+(** JSON/YAML codec that accepts any string.
8383+8484+ Use this when parsing CFF files that may contain non-standard
8585+ country codes. *)
+56
project/ocaml-cff/lib/cff_date.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Date handling for CFF using Ptime. *)
77+88+type t = Ptime.date
99+1010+let of_string s =
1111+ (* CFF dates are YYYY-MM-DD format *)
1212+ match String.split_on_char '-' s with
1313+ | [y; m; d] ->
1414+ (match int_of_string_opt y, int_of_string_opt m, int_of_string_opt d with
1515+ | Some year, Some month, Some day ->
1616+ (* Validate the date components *)
1717+ if year >= 0 && year <= 9999 &&
1818+ month >= 1 && month <= 12 &&
1919+ day >= 1 && day <= 31 then
2020+ Ok (year, month, day)
2121+ else
2222+ Error (`Invalid_date s)
2323+ | _ -> Error (`Invalid_date s))
2424+ | _ -> Error (`Invalid_date s)
2525+2626+let to_string (year, month, day) =
2727+ Printf.sprintf "%04d-%02d-%02d" year month day
2828+2929+let year (y, _, _) = y
3030+let month (_, m, _) = m
3131+let day (_, _, d) = d
3232+3333+let equal (y1, m1, d1) (y2, m2, d2) =
3434+ y1 = y2 && m1 = m2 && d1 = d2
3535+3636+let compare (y1, m1, d1) (y2, m2, d2) =
3737+ match Int.compare y1 y2 with
3838+ | 0 -> (match Int.compare m1 m2 with
3939+ | 0 -> Int.compare d1 d2
4040+ | n -> n)
4141+ | n -> n
4242+4343+let pp ppf date =
4444+ Format.pp_print_string ppf (to_string date)
4545+4646+(* Jsont codec for dates *)
4747+let jsont =
4848+ let dec s =
4949+ match of_string s with
5050+ | Ok d -> d
5151+ | Error (`Invalid_date s) ->
5252+ Jsont.Error.msgf Jsont.Meta.none "Invalid date format: %s" s
5353+ in
5454+ let enc date = to_string date in
5555+ Jsont.string
5656+ |> Jsont.map ~dec ~enc
+87
project/ocaml-cff/lib/cff_date.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Date handling for CFF.
77+88+ CFF uses ISO 8601 date format ([YYYY-MM-DD]) for all date fields.
99+ This module wraps {!Ptime.date} for date representation and provides
1010+ parsing and formatting functions.
1111+1212+ {1 Date Fields in CFF}
1313+1414+ CFF has several date-related fields at different levels:
1515+1616+ {2 Root Level}
1717+1818+ - [date-released]: When the software/dataset was released
1919+2020+ {2 Reference Level}
2121+2222+ - [date-accessed]: When an online resource was accessed
2323+ - [date-downloaded]: When a resource was downloaded
2424+ - [date-published]: Formal publication date
2525+ - [date-released]: Release date (for software references)
2626+2727+ {2 Entity Level}
2828+2929+ - [date-start]: Event start date (for conferences)
3030+ - [date-end]: Event end date (for conferences)
3131+3232+ {1 Date Format}
3333+3434+ All dates use ISO 8601 format: [YYYY-MM-DD]
3535+3636+ {2 Examples}
3737+3838+ {[
3939+ date-released: 2024-01-15
4040+ date-accessed: 2024-06-30
4141+ ]}
4242+4343+ {1 Year-Only Dates}
4444+4545+ For historical works or when only the year is known, use the [year]
4646+ field (an integer) instead of a full date. *)
4747+4848+type t = Ptime.date
4949+(** A date as [(year, month, day)] tuple.
5050+5151+ The tuple contains:
5252+ - [year]: Four-digit year (e.g., [2024])
5353+ - [month]: Month number (1-12)
5454+ - [day]: Day of month (1-31) *)
5555+5656+val of_string : string -> (t, [> `Invalid_date of string]) result
5757+(** Parse a date from [YYYY-MM-DD] format.
5858+5959+ Returns [Error (`Invalid_date s)] if the string is not a valid date.
6060+ Validates that the date is a real calendar date (e.g., rejects Feb 30). *)
6161+6262+val to_string : t -> string
6363+(** Format a date as [YYYY-MM-DD]. *)
6464+6565+val year : t -> int
6666+(** Extract the year component. *)
6767+6868+val month : t -> int
6969+(** Extract the month component (1-12). *)
7070+7171+val day : t -> int
7272+(** Extract the day component (1-31). *)
7373+7474+val equal : t -> t -> bool
7575+(** Date equality. *)
7676+7777+val compare : t -> t -> int
7878+(** Date comparison (chronological order). *)
7979+8080+val pp : Format.formatter -> t -> unit
8181+(** Pretty-print a date in [YYYY-MM-DD] format. *)
8282+8383+val jsont : t Jsont.t
8484+(** JSON/YAML codec for dates.
8585+8686+ Parses strings in [YYYY-MM-DD] format and serializes back to the
8787+ same format. *)
+241
project/ocaml-cff/lib/cff_enums.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Enumeration types for CFF using polymorphic variants. *)
77+88+(** Functor to generate common enum operations. *)
99+module type STRING_ENUM = sig
1010+ type t
1111+ val of_string : string -> t option
1212+ val to_string : t -> string
1313+ val type_name : string
1414+end
1515+1616+module Make_enum (E : STRING_ENUM) = struct
1717+ include E
1818+ let equal (a : t) (b : t) = a = b
1919+ let compare = Stdlib.compare
2020+ let pp ppf t = Format.pp_print_string ppf (to_string t)
2121+ let jsont =
2222+ Jsont.string |> Jsont.map
2323+ ~dec:(fun s ->
2424+ match of_string s with
2525+ | Some t -> t
2626+ | None -> Jsont.Error.msgf Jsont.Meta.none "Invalid %s: %s" type_name s)
2727+ ~enc:to_string
2828+end
2929+3030+module Identifier_type = Make_enum (struct
3131+ type t = [ `Doi | `Url | `Swh | `Other ]
3232+ let type_name = "identifier type"
3333+3434+ let of_string = function
3535+ | "doi" -> Some `Doi
3636+ | "url" -> Some `Url
3737+ | "swh" -> Some `Swh
3838+ | "other" -> Some `Other
3939+ | _ -> None
4040+4141+ let to_string = function
4242+ | `Doi -> "doi"
4343+ | `Url -> "url"
4444+ | `Swh -> "swh"
4545+ | `Other -> "other"
4646+end)
4747+4848+module Reference_type = Make_enum (struct
4949+ type t = [
5050+ | `Art
5151+ | `Article
5252+ | `Audiovisual
5353+ | `Bill
5454+ | `Blog
5555+ | `Book
5656+ | `Catalogue
5757+ | `Conference
5858+ | `Conference_paper
5959+ | `Data
6060+ | `Database
6161+ | `Dictionary
6262+ | `Edited_work
6363+ | `Encyclopedia
6464+ | `Film_broadcast
6565+ | `Generic
6666+ | `Government_document
6767+ | `Grant
6868+ | `Hearing
6969+ | `Historical_work
7070+ | `Legal_case
7171+ | `Legal_rule
7272+ | `Magazine_article
7373+ | `Manual
7474+ | `Map
7575+ | `Multimedia
7676+ | `Music
7777+ | `Newspaper_article
7878+ | `Pamphlet
7979+ | `Patent
8080+ | `Personal_communication
8181+ | `Proceedings
8282+ | `Report
8383+ | `Serial
8484+ | `Slides
8585+ | `Software
8686+ | `Software_code
8787+ | `Software_container
8888+ | `Software_executable
8989+ | `Software_virtual_machine
9090+ | `Sound_recording
9191+ | `Standard
9292+ | `Statute
9393+ | `Thesis
9494+ | `Unpublished
9595+ | `Video
9696+ | `Website
9797+ ]
9898+ let type_name = "reference type"
9999+100100+ let of_string = function
101101+ | "art" -> Some `Art
102102+ | "article" -> Some `Article
103103+ | "audiovisual" -> Some `Audiovisual
104104+ | "bill" -> Some `Bill
105105+ | "blog" -> Some `Blog
106106+ | "book" -> Some `Book
107107+ | "catalogue" -> Some `Catalogue
108108+ | "conference" -> Some `Conference
109109+ | "conference-paper" -> Some `Conference_paper
110110+ | "data" -> Some `Data
111111+ | "database" -> Some `Database
112112+ | "dictionary" -> Some `Dictionary
113113+ | "edited-work" -> Some `Edited_work
114114+ | "encyclopedia" -> Some `Encyclopedia
115115+ | "film-broadcast" -> Some `Film_broadcast
116116+ | "generic" -> Some `Generic
117117+ | "government-document" -> Some `Government_document
118118+ | "grant" -> Some `Grant
119119+ | "hearing" -> Some `Hearing
120120+ | "historical-work" -> Some `Historical_work
121121+ | "legal-case" -> Some `Legal_case
122122+ | "legal-rule" -> Some `Legal_rule
123123+ | "magazine-article" -> Some `Magazine_article
124124+ | "manual" -> Some `Manual
125125+ | "map" -> Some `Map
126126+ | "multimedia" -> Some `Multimedia
127127+ | "music" -> Some `Music
128128+ | "newspaper-article" -> Some `Newspaper_article
129129+ | "pamphlet" -> Some `Pamphlet
130130+ | "patent" -> Some `Patent
131131+ | "personal-communication" -> Some `Personal_communication
132132+ | "proceedings" -> Some `Proceedings
133133+ | "report" -> Some `Report
134134+ | "serial" -> Some `Serial
135135+ | "slides" -> Some `Slides
136136+ | "software" -> Some `Software
137137+ | "software-code" -> Some `Software_code
138138+ | "software-container" -> Some `Software_container
139139+ | "software-executable" -> Some `Software_executable
140140+ | "software-virtual-machine" -> Some `Software_virtual_machine
141141+ | "sound-recording" -> Some `Sound_recording
142142+ | "standard" -> Some `Standard
143143+ | "statute" -> Some `Statute
144144+ | "thesis" -> Some `Thesis
145145+ | "unpublished" -> Some `Unpublished
146146+ | "video" -> Some `Video
147147+ | "website" -> Some `Website
148148+ | _ -> None
149149+150150+ let to_string = function
151151+ | `Art -> "art"
152152+ | `Article -> "article"
153153+ | `Audiovisual -> "audiovisual"
154154+ | `Bill -> "bill"
155155+ | `Blog -> "blog"
156156+ | `Book -> "book"
157157+ | `Catalogue -> "catalogue"
158158+ | `Conference -> "conference"
159159+ | `Conference_paper -> "conference-paper"
160160+ | `Data -> "data"
161161+ | `Database -> "database"
162162+ | `Dictionary -> "dictionary"
163163+ | `Edited_work -> "edited-work"
164164+ | `Encyclopedia -> "encyclopedia"
165165+ | `Film_broadcast -> "film-broadcast"
166166+ | `Generic -> "generic"
167167+ | `Government_document -> "government-document"
168168+ | `Grant -> "grant"
169169+ | `Hearing -> "hearing"
170170+ | `Historical_work -> "historical-work"
171171+ | `Legal_case -> "legal-case"
172172+ | `Legal_rule -> "legal-rule"
173173+ | `Magazine_article -> "magazine-article"
174174+ | `Manual -> "manual"
175175+ | `Map -> "map"
176176+ | `Multimedia -> "multimedia"
177177+ | `Music -> "music"
178178+ | `Newspaper_article -> "newspaper-article"
179179+ | `Pamphlet -> "pamphlet"
180180+ | `Patent -> "patent"
181181+ | `Personal_communication -> "personal-communication"
182182+ | `Proceedings -> "proceedings"
183183+ | `Report -> "report"
184184+ | `Serial -> "serial"
185185+ | `Slides -> "slides"
186186+ | `Software -> "software"
187187+ | `Software_code -> "software-code"
188188+ | `Software_container -> "software-container"
189189+ | `Software_executable -> "software-executable"
190190+ | `Software_virtual_machine -> "software-virtual-machine"
191191+ | `Sound_recording -> "sound-recording"
192192+ | `Standard -> "standard"
193193+ | `Statute -> "statute"
194194+ | `Thesis -> "thesis"
195195+ | `Unpublished -> "unpublished"
196196+ | `Video -> "video"
197197+ | `Website -> "website"
198198+end)
199199+200200+module Status = Make_enum (struct
201201+ type t = [
202202+ | `Abstract
203203+ | `Advance_online
204204+ | `In_preparation
205205+ | `In_press
206206+ | `Preprint
207207+ | `Submitted
208208+ ]
209209+ let type_name = "status"
210210+211211+ let of_string = function
212212+ | "abstract" -> Some `Abstract
213213+ | "advance-online" -> Some `Advance_online
214214+ | "in-preparation" -> Some `In_preparation
215215+ | "in-press" -> Some `In_press
216216+ | "preprint" -> Some `Preprint
217217+ | "submitted" -> Some `Submitted
218218+ | _ -> None
219219+220220+ let to_string = function
221221+ | `Abstract -> "abstract"
222222+ | `Advance_online -> "advance-online"
223223+ | `In_preparation -> "in-preparation"
224224+ | `In_press -> "in-press"
225225+ | `Preprint -> "preprint"
226226+ | `Submitted -> "submitted"
227227+end)
228228+229229+module Cff_type = Make_enum (struct
230230+ type t = [ `Software | `Dataset ]
231231+ let type_name = "CFF type"
232232+233233+ let of_string = function
234234+ | "software" -> Some `Software
235235+ | "dataset" -> Some `Dataset
236236+ | _ -> None
237237+238238+ let to_string = function
239239+ | `Software -> "software"
240240+ | `Dataset -> "dataset"
241241+end)
+289
project/ocaml-cff/lib/cff_enums.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Enumeration types for CFF.
77+88+ CFF defines several enumerated types using fixed string values.
99+ This module represents them as polymorphic variants for type safety
1010+ while providing bidirectional conversion to/from strings.
1111+1212+ {1 Identifier Types}
1313+1414+ The [identifiers] field allows typed references to external resources.
1515+1616+ {1 Reference Types}
1717+1818+ CFF supports 40+ reference types for bibliographic entries, covering
1919+ academic publications, software, data, legal documents, and media.
2020+2121+ {1 Publication Status}
2222+2323+ Works in progress can have a status indicating their publication stage.
2424+2525+ {1 CFF Type}
2626+2727+ The top-level CFF file describes either software or a dataset. *)
2828+2929+(** Identifier type for the [identifiers] field.
3030+3131+ Each identifier in the [identifiers] list has a type indicating the
3232+ identifier scheme:
3333+3434+ - [`Doi] - Digital Object Identifier ({{:https://doi.org}doi.org})
3535+ - [`Url] - Web URL
3636+ - [`Swh] - Software Heritage identifier ({{:https://www.softwareheritage.org}softwareheritage.org})
3737+ - [`Other] - Any other identifier type
3838+3939+ {2 Examples}
4040+4141+ {[
4242+ type: doi
4343+ value: 10.5281/zenodo.1234567
4444+ description: The concept DOI for all versions
4545+4646+ type: swh
4747+ value: swh:1:dir:bc286860f423ea7ced246ba7458eef4b4541cf2d
4848+ description: Software Heritage archive
4949+ ]} *)
5050+module Identifier_type : sig
5151+ type t = [ `Doi | `Url | `Swh | `Other ]
5252+ (** Identifier types. *)
5353+5454+ val of_string : string -> t option
5555+ (** Parse from YAML string: ["doi"], ["url"], ["swh"], ["other"]. *)
5656+5757+ val to_string : t -> string
5858+ (** Convert to YAML string representation. *)
5959+6060+ val equal : t -> t -> bool
6161+ val compare : t -> t -> int
6262+ val pp : Format.formatter -> t -> unit
6363+6464+ val jsont : t Jsont.t
6565+ (** JSON/YAML codec. *)
6666+end
6767+6868+(** Reference type for bibliographic entries.
6969+7070+ CFF 1.2.0 supports 40+ reference types covering virtually all forms
7171+ of citable content. The type determines which fields are relevant.
7272+7373+ {2 Academic/Research}
7474+7575+ - [`Article] - Journal article
7676+ - [`Book] - Complete book
7777+ - [`Conference] - Conference as an event
7878+ - [`Conference_paper] - Paper in conference proceedings
7979+ - [`Edited_work] - Edited collection
8080+ - [`Proceedings] - Conference proceedings volume
8181+ - [`Thesis] - Dissertation or thesis
8282+ - [`Report] - Technical report
8383+8484+ {2 Software}
8585+8686+ - [`Software] - General software (default for CFF files)
8787+ - [`Software_code] - Source code specifically
8888+ - [`Software_container] - Container image (Docker, etc.)
8989+ - [`Software_executable] - Binary/executable
9090+ - [`Software_virtual_machine] - VM image
9191+9292+ {2 Data}
9393+9494+ - [`Data] - General data
9595+ - [`Database] - Database
9696+ - [`Dictionary] - Dictionary or lexicon
9797+ - [`Encyclopedia] - Encyclopedia
9898+9999+ {2 Legal}
100100+101101+ - [`Patent] - Patent
102102+ - [`Legal_case] - Legal case
103103+ - [`Legal_rule] - Legal rule or regulation
104104+ - [`Statute] - Statute or law
105105+ - [`Bill] - Legislative bill
106106+ - [`Hearing] - Legislative hearing
107107+108108+ {2 Media}
109109+110110+ - [`Audiovisual] - Audio/video content
111111+ - [`Film_broadcast] - Film or broadcast
112112+ - [`Video] - Video
113113+ - [`Sound_recording] - Audio recording
114114+ - [`Music] - Musical work
115115+ - [`Art] - Artwork
116116+117117+ {2 Publications}
118118+119119+ - [`Magazine_article] - Magazine article
120120+ - [`Newspaper_article] - Newspaper article
121121+ - [`Blog] - Blog post
122122+ - [`Website] - Website
123123+ - [`Pamphlet] - Pamphlet or brochure
124124+ - [`Serial] - Serial publication
125125+ - [`Manual] - Manual or documentation
126126+ - [`Catalogue] - Catalogue
127127+128128+ {2 Other}
129129+130130+ - [`Generic] - Generic reference (fallback)
131131+ - [`Grant] - Research grant
132132+ - [`Government_document] - Government document
133133+ - [`Historical_work] - Historical work
134134+ - [`Map] - Map
135135+ - [`Multimedia] - Multimedia work
136136+ - [`Personal_communication] - Personal communication
137137+ - [`Slides] - Presentation slides
138138+ - [`Standard] - Technical standard
139139+ - [`Unpublished] - Unpublished work *)
140140+module Reference_type : sig
141141+ type t = [
142142+ | `Art
143143+ | `Article
144144+ | `Audiovisual
145145+ | `Bill
146146+ | `Blog
147147+ | `Book
148148+ | `Catalogue
149149+ | `Conference
150150+ | `Conference_paper
151151+ | `Data
152152+ | `Database
153153+ | `Dictionary
154154+ | `Edited_work
155155+ | `Encyclopedia
156156+ | `Film_broadcast
157157+ | `Generic
158158+ | `Government_document
159159+ | `Grant
160160+ | `Hearing
161161+ | `Historical_work
162162+ | `Legal_case
163163+ | `Legal_rule
164164+ | `Magazine_article
165165+ | `Manual
166166+ | `Map
167167+ | `Multimedia
168168+ | `Music
169169+ | `Newspaper_article
170170+ | `Pamphlet
171171+ | `Patent
172172+ | `Personal_communication
173173+ | `Proceedings
174174+ | `Report
175175+ | `Serial
176176+ | `Slides
177177+ | `Software
178178+ | `Software_code
179179+ | `Software_container
180180+ | `Software_executable
181181+ | `Software_virtual_machine
182182+ | `Sound_recording
183183+ | `Standard
184184+ | `Statute
185185+ | `Thesis
186186+ | `Unpublished
187187+ | `Video
188188+ | `Website
189189+ ]
190190+ (** All supported reference types. *)
191191+192192+ val of_string : string -> t option
193193+ (** Parse from YAML string. Hyphenated names like ["conference-paper"]
194194+ map to underscored variants like [`Conference_paper]. *)
195195+196196+ val to_string : t -> string
197197+ (** Convert to YAML string representation.
198198+ Underscored variants like [`Conference_paper] become ["conference-paper"]. *)
199199+200200+ val equal : t -> t -> bool
201201+ val compare : t -> t -> int
202202+ val pp : Format.formatter -> t -> unit
203203+204204+ val jsont : t Jsont.t
205205+ (** JSON/YAML codec. *)
206206+end
207207+208208+(** Publication status for works in progress.
209209+210210+ The [status] field indicates the publication stage of a work that
211211+ is not yet formally published:
212212+213213+ - [`Abstract] - Only an abstract is available
214214+ - [`Advance_online] - Published online ahead of print
215215+ - [`In_preparation] - Being written
216216+ - [`In_press] - Accepted, awaiting publication
217217+ - [`Preprint] - Available as preprint (arXiv, bioRxiv, etc.)
218218+ - [`Submitted] - Submitted for review
219219+220220+ {2 Example}
221221+222222+ {[
223223+ references:
224224+ - type: article
225225+ title: "Our Upcoming Paper"
226226+ authors:
227227+ - family-names: Smith
228228+ given-names: Jane
229229+ journal: "Nature"
230230+ status: submitted
231231+ ]} *)
232232+module Status : sig
233233+ type t = [
234234+ | `Abstract
235235+ | `Advance_online
236236+ | `In_preparation
237237+ | `In_press
238238+ | `Preprint
239239+ | `Submitted
240240+ ]
241241+ (** Publication status values. *)
242242+243243+ val of_string : string -> t option
244244+ (** Parse from YAML string: ["abstract"], ["advance-online"], etc. *)
245245+246246+ val to_string : t -> string
247247+ (** Convert to YAML string representation. *)
248248+249249+ val equal : t -> t -> bool
250250+ val compare : t -> t -> int
251251+ val pp : Format.formatter -> t -> unit
252252+253253+ val jsont : t Jsont.t
254254+ (** JSON/YAML codec. *)
255255+end
256256+257257+(** CFF file type: software or dataset.
258258+259259+ The [type] field at the root level indicates whether the CFF file
260260+ describes software or a dataset:
261261+262262+ - [`Software] - Software project (default if omitted)
263263+ - [`Dataset] - Dataset or data package
264264+265265+ {2 Example}
266266+267267+ {[
268268+ cff-version: "1.2.0"
269269+ type: dataset
270270+ title: "Climate Data 2020-2024"
271271+ # ...
272272+ ]} *)
273273+module Cff_type : sig
274274+ type t = [ `Software | `Dataset ]
275275+ (** CFF file types. *)
276276+277277+ val of_string : string -> t option
278278+ (** Parse from YAML string: ["software"] or ["dataset"]. *)
279279+280280+ val to_string : t -> string
281281+ (** Convert to YAML string representation. *)
282282+283283+ val equal : t -> t -> bool
284284+ val compare : t -> t -> int
285285+ val pp : Format.formatter -> t -> unit
286286+287287+ val jsont : t Jsont.t
288288+ (** JSON/YAML codec. *)
289289+end
+45
project/ocaml-cff/lib/cff_identifier.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Identifier type for CFF. *)
77+88+type t = {
99+ type_ : Cff_enums.Identifier_type.t;
1010+ value : string;
1111+ description : string option;
1212+}
1313+1414+let make ~type_ ~value ?description () =
1515+ { type_; value; description }
1616+1717+let type_ t = t.type_
1818+let value t = t.value
1919+let description t = t.description
2020+2121+let equal a b =
2222+ Cff_enums.Identifier_type.equal a.type_ b.type_ &&
2323+ String.equal a.value b.value
2424+2525+let compare a b =
2626+ match Cff_enums.Identifier_type.compare a.type_ b.type_ with
2727+ | 0 -> String.compare a.value b.value
2828+ | n -> n
2929+3030+let pp ppf t =
3131+ Format.fprintf ppf "%a: %s"
3232+ Cff_enums.Identifier_type.pp t.type_
3333+ t.value
3434+3535+let jsont =
3636+ Jsont.Object.map ~kind:"Identifier"
3737+ (fun type_ value description -> { type_; value; description })
3838+ |> Jsont.Object.mem "type" Cff_enums.Identifier_type.jsont
3939+ ~enc:(fun i -> i.type_)
4040+ |> Jsont.Object.mem "value" Jsont.string
4141+ ~enc:(fun i -> i.value)
4242+ |> Jsont.Object.opt_mem "description" Jsont.string
4343+ ~enc:(fun i -> i.description)
4444+ |> Jsont.Object.skip_unknown
4545+ |> Jsont.Object.finish
+110
project/ocaml-cff/lib/cff_identifier.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Typed identifiers for CFF.
77+88+ The [identifiers] field in CFF allows listing multiple typed
99+ identifiers for a work. Each identifier has a type, value, and
1010+ optional description.
1111+1212+ {1 Identifier Types}
1313+1414+ CFF supports four identifier types:
1515+1616+ - {b DOI}: Digital Object Identifier
1717+ ({{:https://doi.org}doi.org})
1818+ - {b URL}: Web URL
1919+ - {b SWH}: Software Heritage identifier
2020+ ({{:https://www.softwareheritage.org}softwareheritage.org})
2121+ - {b Other}: Any other identifier scheme
2222+2323+ {1 Usage}
2424+2525+ The [identifiers] field is a list, allowing multiple identifiers:
2626+2727+ {[
2828+ identifiers:
2929+ - type: doi
3030+ value: 10.5281/zenodo.1234567
3131+ description: The concept DOI for all versions
3232+3333+ - type: doi
3434+ value: 10.5281/zenodo.1234568
3535+ description: The DOI for version 1.0.0
3636+3737+ - type: swh
3838+ value: swh:1:dir:bc286860f423ea7ced246ba7458eef4b4541cf2d
3939+ description: Software Heritage archive
4040+4141+ - type: url
4242+ value: https://github.com/user/project/releases/tag/v1.0.0
4343+ description: Release on GitHub
4444+ ]}
4545+4646+ {1 DOI vs doi Field}
4747+4848+ CFF provides two ways to specify DOIs:
4949+5050+ - The [doi] field at root level: A single, primary DOI
5151+ - The [identifiers] field with [type: doi]: Multiple DOIs with descriptions
5252+5353+ Both can be used together; [identifiers] provides more detail.
5454+5555+ {1 Software Heritage}
5656+5757+ Software Heritage (SWH) provides persistent identifiers for source
5858+ code. SWH identifiers follow the format:
5959+6060+ [swh:1:<object_type>:<hash>]
6161+6262+ Where object_type can be:
6363+ - [cnt]: Content (file)
6464+ - [dir]: Directory
6565+ - [rev]: Revision (commit)
6666+ - [rel]: Release
6767+ - [snp]: Snapshot *)
6868+6969+type t
7070+(** An identifier with type, value, and optional description. *)
7171+7272+val make :
7373+ type_:Cff_enums.Identifier_type.t ->
7474+ value:string ->
7575+ ?description:string ->
7676+ unit -> t
7777+(** Create an identifier.
7878+7979+ @param type_ The identifier type ([`Doi], [`Url], [`Swh], or [`Other])
8080+ @param value The identifier value (DOI, URL, SWH ID, etc.)
8181+ @param description Optional human-readable description *)
8282+8383+val type_ : t -> Cff_enums.Identifier_type.t
8484+(** The identifier type. *)
8585+8686+val value : t -> string
8787+(** The identifier value.
8888+8989+ For DOIs, this is just the DOI (e.g., ["10.5281/zenodo.1234567"]),
9090+ not the full URL. *)
9191+9292+val description : t -> string option
9393+(** Optional description explaining what this identifier refers to.
9494+9595+ Examples:
9696+ - ["The concept DOI for all versions"]
9797+ - ["Version 1.0.0 archive"]
9898+ - ["Release on GitHub"] *)
9999+100100+val equal : t -> t -> bool
101101+(** Identifier equality (compares all fields). *)
102102+103103+val compare : t -> t -> int
104104+(** Identifier comparison. *)
105105+106106+val pp : Format.formatter -> t -> unit
107107+(** Pretty-print as "[type]: value (description)". *)
108108+109109+val jsont : t Jsont.t
110110+(** JSON/YAML codec for identifiers. *)
+145
project/ocaml-cff/lib/cff_license.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** SPDX license handling for CFF. *)
77+88+module Id = struct
99+ type t = string
1010+1111+ (* Case-insensitive lookup in valid license IDs *)
1212+ let uppercased_valid_ids =
1313+ List.map (fun x -> (x, String.uppercase_ascii x)) Spdx_licenses.valid_license_ids
1414+1515+ let of_string s =
1616+ let s_upper = String.uppercase_ascii s in
1717+ match List.find_opt (fun (_, up) -> String.equal s_upper up) uppercased_valid_ids with
1818+ | Some (canonical, _) -> Ok canonical
1919+ | None -> Error (`Invalid_license_id s)
2020+2121+ let to_string t = t
2222+2323+ let equal = String.equal
2424+ let compare = String.compare
2525+2626+ let pp ppf t = Format.pp_print_string ppf t
2727+end
2828+2929+type t = Id.t list (* Non-empty list; multiple = OR relationship *)
3030+3131+let single id = [id]
3232+let multiple ids = ids
3333+3434+let ids t = t
3535+3636+let is_single = function
3737+ | [_] -> true
3838+ | _ -> false
3939+4040+let of_string s = Result.map single (Id.of_string s)
4141+4242+let of_string_list ss =
4343+ let rec aux acc = function
4444+ | [] -> Ok (List.rev acc)
4545+ | s :: rest ->
4646+ match Id.of_string s with
4747+ | Ok id -> aux (id :: acc) rest
4848+ | Error e -> Error e
4949+ in
5050+ match ss with
5151+ | [] -> Error (`Invalid_license_id "empty license list")
5252+ | ss -> aux [] ss
5353+5454+let to_string_list t = t
5555+5656+let equal t1 t2 =
5757+ List.length t1 = List.length t2 &&
5858+ List.for_all2 Id.equal t1 t2
5959+6060+let compare t1 t2 =
6161+ List.compare Id.compare t1 t2
6262+6363+let pp ppf t =
6464+ match t with
6565+ | [id] -> Id.pp ppf id
6666+ | ids ->
6767+ Format.fprintf ppf "[%a]"
6868+ (Format.pp_print_list ~pp_sep:(fun ppf () -> Format.fprintf ppf ", ") Id.pp)
6969+ ids
7070+7171+(* Convert to Spdx_licenses.t (OR combination) *)
7272+let to_spdx t =
7373+ let rec build = function
7474+ | [] -> assert false (* t is non-empty *)
7575+ | [id] -> Spdx_licenses.Simple (Spdx_licenses.LicenseID id)
7676+ | id :: rest ->
7777+ Spdx_licenses.OR (Spdx_licenses.Simple (Spdx_licenses.LicenseID id), build rest)
7878+ in
7979+ build t
8080+8181+(* Convert from Spdx_licenses.t (only simple IDs and OR combinations) *)
8282+let of_spdx spdx =
8383+ let rec extract acc = function
8484+ | Spdx_licenses.Simple (Spdx_licenses.LicenseID id) ->
8585+ Ok (id :: acc)
8686+ | Spdx_licenses.Simple (Spdx_licenses.LicenseIDPlus _) ->
8787+ Error `Unsupported_expression
8888+ | Spdx_licenses.Simple (Spdx_licenses.LicenseRef _) ->
8989+ Error `Unsupported_expression
9090+ | Spdx_licenses.WITH _ ->
9191+ Error `Unsupported_expression
9292+ | Spdx_licenses.AND _ ->
9393+ Error `Unsupported_expression
9494+ | Spdx_licenses.OR (left, right) ->
9595+ Result.bind (extract acc left) (fun acc -> extract acc right)
9696+ in
9797+ Result.map List.rev (extract [] spdx)
9898+9999+(* Jsont codec - handles both single string and array of strings *)
100100+let jsont =
101101+ let string_codec =
102102+ Jsont.string |> Jsont.map
103103+ ~dec:(fun s ->
104104+ match Id.of_string s with
105105+ | Ok id -> [id]
106106+ | Error (`Invalid_license_id s) ->
107107+ Jsont.Error.msgf Jsont.Meta.none "Invalid SPDX license ID: %s" s)
108108+ ~enc:(function
109109+ | [id] -> id
110110+ | _ -> assert false) (* Only used for single-element lists *)
111111+ in
112112+ let array_codec =
113113+ Jsont.(array string) |> Jsont.map
114114+ ~dec:(fun ss ->
115115+ match of_string_list (Stdlib.Array.to_list ss) with
116116+ | Ok t -> t
117117+ | Error (`Invalid_license_id s) ->
118118+ Jsont.Error.msgf Jsont.Meta.none "Invalid SPDX license ID: %s" s)
119119+ ~enc:(fun t -> Stdlib.Array.of_list t)
120120+ in
121121+ Jsont.any
122122+ ~dec_string:string_codec
123123+ ~dec_array:array_codec
124124+ ~enc:(fun t ->
125125+ match t with
126126+ | [_] -> string_codec
127127+ | _ -> array_codec)
128128+ ()
129129+130130+(* Lenient codec that accepts any string/array without validation *)
131131+let jsont_lenient =
132132+ let string_codec =
133133+ Jsont.string |> Jsont.map ~dec:(fun s -> [s]) ~enc:(function [s] -> s | _ -> assert false)
134134+ in
135135+ let array_codec =
136136+ Jsont.(array string) |> Jsont.map ~dec:(fun ss -> Stdlib.Array.to_list ss) ~enc:(fun t -> Stdlib.Array.of_list t)
137137+ in
138138+ Jsont.any
139139+ ~dec_string:string_codec
140140+ ~dec_array:array_codec
141141+ ~enc:(fun t ->
142142+ match t with
143143+ | [_] -> string_codec
144144+ | _ -> array_codec)
145145+ ()
+159
project/ocaml-cff/lib/cff_license.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** SPDX license identifiers for CFF.
77+88+ CFF uses {{:https://spdx.org/licenses/}SPDX license identifiers}
99+ for the [license] field. SPDX provides a standardized list of
1010+ open source license identifiers.
1111+1212+ {1 License Field}
1313+1414+ The [license] field can be a single license identifier like ["MIT"],
1515+ or a list of licenses with OR relationship like ["GPL-3.0-only"] and
1616+ ["MIT"] together.
1717+1818+ When multiple licenses are listed, it means the user may choose
1919+ {b any one} of the listed licenses. This matches the SPDX OR
2020+ semantics.
2121+2222+ {1 Examples}
2323+2424+ {2 Single License}
2525+2626+ {[
2727+ cff-version: "1.2.0"
2828+ title: "My Project"
2929+ license: MIT
3030+ ]}
3131+3232+ {2 Multiple Licenses (OR)}
3333+3434+ {[
3535+ cff-version: "1.2.0"
3636+ title: "My Project"
3737+ license:
3838+ - Apache-2.0
3939+ - MIT
4040+ ]}
4141+4242+ This means the software is available under Apache-2.0 OR MIT.
4343+4444+ {1 Common License IDs}
4545+4646+ Some commonly used SPDX license identifiers:
4747+4848+ - [MIT] - MIT License
4949+ - [Apache-2.0] - Apache License 2.0
5050+ - [GPL-3.0-only] - GNU General Public License v3.0 only
5151+ - [GPL-3.0-or-later] - GNU GPL v3.0 or later
5252+ - [BSD-2-Clause] - BSD 2-Clause "Simplified" License
5353+ - [BSD-3-Clause] - BSD 3-Clause "New" License
5454+ - [ISC] - ISC License
5555+ - [MPL-2.0] - Mozilla Public License 2.0
5656+ - [LGPL-3.0-only] - GNU Lesser GPL v3.0
5757+ - [CC-BY-4.0] - Creative Commons Attribution 4.0
5858+5959+ {1 Deprecated IDs}
6060+6161+ Some older license identifiers are deprecated in SPDX:
6262+6363+ - [GPL-2.0] should use [GPL-2.0-only] or [GPL-2.0-or-later]
6464+ - [GPL-3.0] should use [GPL-3.0-only] or [GPL-3.0-or-later]
6565+ - [LGPL-2.1] should use [LGPL-2.1-only] or [LGPL-2.1-or-later]
6666+6767+ The {!jsont_lenient} codec accepts these deprecated IDs. *)
6868+6969+(** A validated SPDX license identifier. *)
7070+module Id : sig
7171+ type t
7272+ (** A single validated SPDX license ID. *)
7373+7474+ val of_string : string -> (t, [> `Invalid_license_id of string]) result
7575+ (** Parse and validate a license ID.
7676+7777+ The check is case-insensitive. Returns [Error] for unknown
7878+ license identifiers. *)
7979+8080+ val to_string : t -> string
8181+ (** Return the canonical (properly cased) license ID string. *)
8282+8383+ val equal : t -> t -> bool
8484+ val compare : t -> t -> int
8585+8686+ val pp : Format.formatter -> t -> unit
8787+ (** Pretty-print the license ID. *)
8888+end
8989+9090+type t
9191+(** A CFF license: one or more SPDX license IDs.
9292+9393+ Multiple IDs represent an OR relationship: the user may choose
9494+ any of the listed licenses. *)
9595+9696+val single : Id.t -> t
9797+(** Create a license from a single ID. *)
9898+9999+val multiple : Id.t list -> t
100100+(** Create a license from multiple IDs (OR relationship).
101101+102102+ Raises [Invalid_argument] if the list is empty. *)
103103+104104+val ids : t -> Id.t list
105105+(** Get the list of license IDs.
106106+107107+ For a single license, returns a one-element list. *)
108108+109109+val is_single : t -> bool
110110+(** [true] if this is a single license ID, [false] for multiple. *)
111111+112112+val of_string : string -> (t, [> `Invalid_license_id of string]) result
113113+(** Parse a single license ID string into a license.
114114+115115+ Equivalent to [Result.map single (Id.of_string s)]. *)
116116+117117+val of_string_list : string list -> (t, [> `Invalid_license_id of string]) result
118118+(** Parse a list of license ID strings.
119119+120120+ All IDs must be valid; returns [Error] if any ID is invalid. *)
121121+122122+val to_string_list : t -> string list
123123+(** Return the list of license ID strings. *)
124124+125125+val equal : t -> t -> bool
126126+(** License equality. *)
127127+128128+val compare : t -> t -> int
129129+(** License comparison. *)
130130+131131+val pp : Format.formatter -> t -> unit
132132+(** Pretty-print: single ID or comma-separated list for multiple. *)
133133+134134+(** {1 SPDX Interop} *)
135135+136136+val to_spdx : t -> Spdx_licenses.t
137137+(** Convert to an SPDX license expression (OR combination). *)
138138+139139+val of_spdx : Spdx_licenses.t -> (t, [> `Unsupported_expression]) result
140140+(** Convert from an SPDX license expression.
141141+142142+ Only simple license IDs and OR combinations are supported.
143143+ Complex expressions using AND, WITH (exceptions), or license
144144+ references return [Error `Unsupported_expression]. *)
145145+146146+(** {1 Codecs} *)
147147+148148+val jsont : t Jsont.t
149149+(** JSON/YAML codec that validates license IDs.
150150+151151+ Handles both single string (["MIT"]) and array of strings.
152152+ Returns an error for invalid SPDX license identifiers. *)
153153+154154+val jsont_lenient : t Jsont.t
155155+(** JSON/YAML codec that accepts any string without validation.
156156+157157+ Use this codec when parsing CFF files that may contain deprecated
158158+ or non-standard license identifiers. Invalid IDs are preserved
159159+ as-is for round-tripping. *)
+597
project/ocaml-cff/lib/cff_reference.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Reference type for CFF with logical sub-records. *)
77+88+(** Core identity of a reference. *)
99+module Core = struct
1010+ type t = {
1111+ type_ : Cff_enums.Reference_type.t;
1212+ title : string;
1313+ authors : Cff_author.t list;
1414+ abstract : string option;
1515+ abbreviation : string option;
1616+ }
1717+1818+ let make ~type_ ~title ~authors ?abstract ?abbreviation () =
1919+ { type_; title; authors; abstract; abbreviation }
2020+2121+ let type_ t = t.type_
2222+ let title t = t.title
2323+ let authors t = t.authors
2424+ let abstract t = t.abstract
2525+ let abbreviation t = t.abbreviation
2626+2727+ let pp ppf t =
2828+ Format.fprintf ppf "%s (%a)"
2929+ t.title Cff_enums.Reference_type.pp t.type_
3030+end
3131+3232+(** Publication information (journal, volume, pages, etc.). *)
3333+module Publication = struct
3434+ type t = {
3535+ journal : string option;
3636+ volume : string option;
3737+ issue : string option;
3838+ pages : string option;
3939+ start : string option;
4040+ end_ : string option;
4141+ edition : string option;
4242+ section : string option;
4343+ status : Cff_enums.Status.t option;
4444+ }
4545+4646+ let empty = {
4747+ journal = None; volume = None; issue = None; pages = None;
4848+ start = None; end_ = None; edition = None; section = None;
4949+ status = None;
5050+ }
5151+5252+ let make ?journal ?volume ?issue ?pages ?start ?end_ ?edition
5353+ ?section ?status () =
5454+ { journal; volume; issue; pages; start; end_; edition; section; status }
5555+5656+ let journal t = t.journal
5757+ let volume t = t.volume
5858+ let issue t = t.issue
5959+ let pages t = t.pages
6060+ let start t = t.start
6161+ let end_ t = t.end_
6262+ let edition t = t.edition
6363+ let section t = t.section
6464+ let status t = t.status
6565+6666+ let is_empty t =
6767+ t.journal = None && t.volume = None && t.issue = None &&
6868+ t.pages = None && t.start = None && t.end_ = None &&
6969+ t.edition = None && t.section = None && t.status = None
7070+end
7171+7272+(** Collection information (proceedings, book series, etc.). *)
7373+module Collection = struct
7474+ type t = {
7575+ collection_title : string option;
7676+ collection_type : string option;
7777+ collection_doi : string option;
7878+ volume_title : string option;
7979+ number_volumes : string option;
8080+ }
8181+8282+ let empty = {
8383+ collection_title = None; collection_type = None;
8484+ collection_doi = None; volume_title = None; number_volumes = None;
8585+ }
8686+8787+ let make ?collection_title ?collection_type ?collection_doi
8888+ ?volume_title ?number_volumes () =
8989+ { collection_title; collection_type; collection_doi;
9090+ volume_title; number_volumes }
9191+9292+ let collection_title t = t.collection_title
9393+ let collection_type t = t.collection_type
9494+ let collection_doi t = t.collection_doi
9595+ let volume_title t = t.volume_title
9696+ let number_volumes t = t.number_volumes
9797+9898+ let is_empty t =
9999+ t.collection_title = None && t.collection_type = None &&
100100+ t.collection_doi = None && t.volume_title = None &&
101101+ t.number_volumes = None
102102+end
103103+104104+(** Date information. *)
105105+module Dates = struct
106106+ type t = {
107107+ date_accessed : Cff_date.t option;
108108+ date_downloaded : Cff_date.t option;
109109+ date_published : Cff_date.t option;
110110+ date_released : Cff_date.t option;
111111+ year : int option;
112112+ year_original : int option;
113113+ month : int option;
114114+ issue_date : string option;
115115+ }
116116+117117+ let empty = {
118118+ date_accessed = None; date_downloaded = None;
119119+ date_published = None; date_released = None;
120120+ year = None; year_original = None; month = None; issue_date = None;
121121+ }
122122+123123+ let make ?date_accessed ?date_downloaded ?date_published ?date_released
124124+ ?year ?year_original ?month ?issue_date () =
125125+ { date_accessed; date_downloaded; date_published; date_released;
126126+ year; year_original; month; issue_date }
127127+128128+ let date_accessed t = t.date_accessed
129129+ let date_downloaded t = t.date_downloaded
130130+ let date_published t = t.date_published
131131+ let date_released t = t.date_released
132132+ let year t = t.year
133133+ let year_original t = t.year_original
134134+ let month t = t.month
135135+ let issue_date t = t.issue_date
136136+137137+ let is_empty t =
138138+ t.date_accessed = None && t.date_downloaded = None &&
139139+ t.date_published = None && t.date_released = None &&
140140+ t.year = None && t.year_original = None &&
141141+ t.month = None && t.issue_date = None
142142+end
143143+144144+(** Identifiers and links. *)
145145+module Identifiers = struct
146146+ type t = {
147147+ doi : string option;
148148+ url : string option;
149149+ repository : string option;
150150+ repository_code : string option;
151151+ repository_artifact : string option;
152152+ isbn : string option;
153153+ issn : string option;
154154+ pmcid : string option;
155155+ nihmsid : string option;
156156+ identifiers : Cff_identifier.t list option;
157157+ }
158158+159159+ let empty = {
160160+ doi = None; url = None; repository = None;
161161+ repository_code = None; repository_artifact = None;
162162+ isbn = None; issn = None; pmcid = None; nihmsid = None;
163163+ identifiers = None;
164164+ }
165165+166166+ let make ?doi ?url ?repository ?repository_code ?repository_artifact
167167+ ?isbn ?issn ?pmcid ?nihmsid ?identifiers () =
168168+ { doi; url; repository; repository_code; repository_artifact;
169169+ isbn; issn; pmcid; nihmsid; identifiers }
170170+171171+ let doi t = t.doi
172172+ let url t = t.url
173173+ let repository t = t.repository
174174+ let repository_code t = t.repository_code
175175+ let repository_artifact t = t.repository_artifact
176176+ let isbn t = t.isbn
177177+ let issn t = t.issn
178178+ let pmcid t = t.pmcid
179179+ let nihmsid t = t.nihmsid
180180+ let identifiers t = t.identifiers
181181+182182+ let is_empty t =
183183+ t.doi = None && t.url = None && t.repository = None &&
184184+ t.repository_code = None && t.repository_artifact = None &&
185185+ t.isbn = None && t.issn = None && t.pmcid = None &&
186186+ t.nihmsid = None && t.identifiers = None
187187+end
188188+189189+(** Related entities (editors, publisher, etc.). *)
190190+module Entities = struct
191191+ type t = {
192192+ editors : Cff_author.t list option;
193193+ editors_series : Cff_author.t list option;
194194+ translators : Cff_author.t list option;
195195+ recipients : Cff_author.t list option;
196196+ senders : Cff_author.t list option;
197197+ contact : Cff_author.t list option;
198198+ publisher : Cff_author.Entity.t option;
199199+ institution : Cff_author.Entity.t option;
200200+ conference : Cff_author.Entity.t option;
201201+ database_provider : Cff_author.Entity.t option;
202202+ location : Cff_author.Entity.t option;
203203+ }
204204+205205+ let empty = {
206206+ editors = None; editors_series = None; translators = None;
207207+ recipients = None; senders = None; contact = None;
208208+ publisher = None; institution = None; conference = None;
209209+ database_provider = None; location = None;
210210+ }
211211+212212+ let make ?editors ?editors_series ?translators ?recipients ?senders
213213+ ?contact ?publisher ?institution ?conference ?database_provider
214214+ ?location () =
215215+ { editors; editors_series; translators; recipients; senders;
216216+ contact; publisher; institution; conference; database_provider;
217217+ location }
218218+219219+ let editors t = t.editors
220220+ let editors_series t = t.editors_series
221221+ let translators t = t.translators
222222+ let recipients t = t.recipients
223223+ let senders t = t.senders
224224+ let contact t = t.contact
225225+ let publisher t = t.publisher
226226+ let institution t = t.institution
227227+ let conference t = t.conference
228228+ let database_provider t = t.database_provider
229229+ let location t = t.location
230230+231231+ let is_empty t =
232232+ t.editors = None && t.editors_series = None && t.translators = None &&
233233+ t.recipients = None && t.senders = None && t.contact = None &&
234234+ t.publisher = None && t.institution = None && t.conference = None &&
235235+ t.database_provider = None && t.location = None
236236+end
237237+238238+(** Metadata and description. *)
239239+module Metadata = struct
240240+ type t = {
241241+ keywords : string list option;
242242+ languages : string list option;
243243+ license : Cff_license.t option;
244244+ license_url : string option;
245245+ copyright : string option;
246246+ scope : string option;
247247+ notes : string option;
248248+ }
249249+250250+ let empty = {
251251+ keywords = None; languages = None; license = None;
252252+ license_url = None; copyright = None; scope = None; notes = None;
253253+ }
254254+255255+ let make ?keywords ?languages ?license ?license_url ?copyright
256256+ ?scope ?notes () =
257257+ { keywords; languages; license; license_url; copyright; scope; notes }
258258+259259+ let keywords t = t.keywords
260260+ let languages t = t.languages
261261+ let license t = t.license
262262+ let license_url t = t.license_url
263263+ let copyright t = t.copyright
264264+ let scope t = t.scope
265265+ let notes t = t.notes
266266+267267+ let is_empty t =
268268+ t.keywords = None && t.languages = None && t.license = None &&
269269+ t.license_url = None && t.copyright = None &&
270270+ t.scope = None && t.notes = None
271271+end
272272+273273+(** Technical and domain-specific fields. *)
274274+module Technical = struct
275275+ type t = {
276276+ commit : string option;
277277+ version : string option;
278278+ filename : string option;
279279+ format : string option;
280280+ medium : string option;
281281+ data_type : string option;
282282+ database : string option;
283283+ number : string option;
284284+ patent_states : string list option;
285285+ thesis_type : string option;
286286+ term : string option;
287287+ entry : string option;
288288+ department : string option;
289289+ loc_start : string option;
290290+ loc_end : string option;
291291+ }
292292+293293+ let empty = {
294294+ commit = None; version = None; filename = None; format = None;
295295+ medium = None; data_type = None; database = None; number = None;
296296+ patent_states = None; thesis_type = None; term = None; entry = None;
297297+ department = None; loc_start = None; loc_end = None;
298298+ }
299299+300300+ let make ?commit ?version ?filename ?format ?medium ?data_type
301301+ ?database ?number ?patent_states ?thesis_type ?term ?entry
302302+ ?department ?loc_start ?loc_end () =
303303+ { commit; version; filename; format; medium; data_type; database;
304304+ number; patent_states; thesis_type; term; entry; department;
305305+ loc_start; loc_end }
306306+307307+ let commit t = t.commit
308308+ let version t = t.version
309309+ let filename t = t.filename
310310+ let format t = t.format
311311+ let medium t = t.medium
312312+ let data_type t = t.data_type
313313+ let database t = t.database
314314+ let number t = t.number
315315+ let patent_states t = t.patent_states
316316+ let thesis_type t = t.thesis_type
317317+ let term t = t.term
318318+ let entry t = t.entry
319319+ let department t = t.department
320320+ let loc_start t = t.loc_start
321321+ let loc_end t = t.loc_end
322322+323323+ let is_empty t =
324324+ t.commit = None && t.version = None && t.filename = None &&
325325+ t.format = None && t.medium = None && t.data_type = None &&
326326+ t.database = None && t.number = None && t.patent_states = None &&
327327+ t.thesis_type = None && t.term = None && t.entry = None &&
328328+ t.department = None && t.loc_start = None && t.loc_end = None
329329+end
330330+331331+(** Complete reference type. *)
332332+type t = {
333333+ core : Core.t;
334334+ publication : Publication.t;
335335+ collection : Collection.t;
336336+ dates : Dates.t;
337337+ identifiers : Identifiers.t;
338338+ entities : Entities.t;
339339+ metadata : Metadata.t;
340340+ technical : Technical.t;
341341+}
342342+343343+let make ~core
344344+ ?(publication = Publication.empty)
345345+ ?(collection = Collection.empty)
346346+ ?(dates = Dates.empty)
347347+ ?(identifiers = Identifiers.empty)
348348+ ?(entities = Entities.empty)
349349+ ?(metadata = Metadata.empty)
350350+ ?(technical = Technical.empty)
351351+ () =
352352+ { core; publication; collection; dates; identifiers;
353353+ entities; metadata; technical }
354354+355355+let make_simple ~type_ ~title ~authors ?doi ?year ?journal () =
356356+ let core = Core.make ~type_ ~title ~authors () in
357357+ let publication = Publication.make ?journal () in
358358+ let dates = Dates.make ?year () in
359359+ let identifiers = Identifiers.make ?doi () in
360360+ make ~core ~publication ~dates ~identifiers ()
361361+362362+(* Accessors for sub-records *)
363363+let core t = t.core
364364+let publication t = t.publication
365365+let collection t = t.collection
366366+let dates t = t.dates
367367+let identifiers t = t.identifiers
368368+let entities t = t.entities
369369+let metadata t = t.metadata
370370+let technical t = t.technical
371371+372372+(* Direct accessors for common fields *)
373373+let type_ t = Core.type_ t.core
374374+let title t = Core.title t.core
375375+let authors t = Core.authors t.core
376376+let doi t = Identifiers.doi t.identifiers
377377+let year t = Dates.year t.dates
378378+379379+let pp ppf t =
380380+ Core.pp ppf t.core
381381+382382+(* Helper for string that can also be int (for pages, etc.) *)
383383+let string_or_int_jsont =
384384+ Jsont.any
385385+ ~dec_number:(Jsont.number |> Jsont.map
386386+ ~dec:(fun f -> string_of_int (int_of_float f))
387387+ ~enc:float_of_string)
388388+ ~dec_string:Jsont.string
389389+ ~enc:(fun s ->
390390+ match float_of_string_opt s with
391391+ | Some _ -> Jsont.number |> Jsont.map ~dec:(fun _ -> assert false) ~enc:float_of_string
392392+ | None -> Jsont.string)
393393+ ()
394394+395395+(* Jsont codec for the full reference type *)
396396+let jsont =
397397+ (* Helper to convert array jsont to list jsont *)
398398+ let list_jsont elt =
399399+ Jsont.(array elt |> map
400400+ ~dec:(fun arr -> Stdlib.Array.to_list arr)
401401+ ~enc:(fun lst -> Stdlib.Array.of_list lst))
402402+ in
403403+ let authors_list_jsont = list_jsont Cff_author.jsont in
404404+ let identifiers_list_jsont = list_jsont Cff_identifier.jsont in
405405+ let string_list_jsont = list_jsont Jsont.string in
406406+ (* We need to decode all 60+ fields and then group into sub-records *)
407407+ Jsont.Object.map ~kind:"Reference"
408408+ (fun type_ title authors abstract abbreviation
409409+ (* Publication *)
410410+ journal volume issue pages start end_ edition section status
411411+ (* Collection *)
412412+ collection_title collection_type collection_doi volume_title number_volumes
413413+ (* Dates *)
414414+ date_accessed date_downloaded date_published date_released
415415+ year year_original month issue_date
416416+ (* Identifiers *)
417417+ doi url repository repository_code repository_artifact
418418+ isbn issn pmcid nihmsid identifiers_list
419419+ (* Entities *)
420420+ editors editors_series translators recipients senders contact
421421+ publisher institution conference database_provider location_entity
422422+ (* Metadata *)
423423+ keywords languages license license_url copyright scope notes
424424+ (* Technical *)
425425+ commit version filename format medium data_type database
426426+ number patent_states thesis_type term entry department
427427+ loc_start loc_end ->
428428+ let core = { Core.type_; title; authors; abstract; abbreviation } in
429429+ let publication = { Publication.journal; volume; issue; pages;
430430+ start; end_; edition; section; status } in
431431+ let collection = { Collection.collection_title; collection_type;
432432+ collection_doi; volume_title; number_volumes } in
433433+ let dates = { Dates.date_accessed; date_downloaded; date_published;
434434+ date_released; year; year_original; month; issue_date } in
435435+ let identifiers = { Identifiers.doi; url; repository; repository_code;
436436+ repository_artifact; isbn; issn; pmcid; nihmsid;
437437+ identifiers = identifiers_list } in
438438+ let entities = { Entities.editors; editors_series; translators;
439439+ recipients; senders; contact; publisher; institution;
440440+ conference; database_provider; location = location_entity } in
441441+ let metadata = { Metadata.keywords; languages; license; license_url;
442442+ copyright; scope; notes } in
443443+ let technical = { Technical.commit; version; filename; format; medium;
444444+ data_type; database; number; patent_states; thesis_type;
445445+ term; entry; department; loc_start; loc_end } in
446446+ { core; publication; collection; dates; identifiers;
447447+ entities; metadata; technical })
448448+ (* Core fields *)
449449+ |> Jsont.Object.mem "type" Cff_enums.Reference_type.jsont
450450+ ~enc:(fun r -> r.core.type_)
451451+ |> Jsont.Object.mem "title" Jsont.string
452452+ ~enc:(fun r -> r.core.title)
453453+ |> Jsont.Object.mem "authors" authors_list_jsont
454454+ ~enc:(fun r -> r.core.authors)
455455+ |> Jsont.Object.opt_mem "abstract" Jsont.string
456456+ ~enc:(fun r -> r.core.abstract)
457457+ |> Jsont.Object.opt_mem "abbreviation" Jsont.string
458458+ ~enc:(fun r -> r.core.abbreviation)
459459+ (* Publication fields *)
460460+ |> Jsont.Object.opt_mem "journal" Jsont.string
461461+ ~enc:(fun r -> r.publication.journal)
462462+ |> Jsont.Object.opt_mem "volume" string_or_int_jsont
463463+ ~enc:(fun r -> r.publication.volume)
464464+ |> Jsont.Object.opt_mem "issue" string_or_int_jsont
465465+ ~enc:(fun r -> r.publication.issue)
466466+ |> Jsont.Object.opt_mem "pages" string_or_int_jsont
467467+ ~enc:(fun r -> r.publication.pages)
468468+ |> Jsont.Object.opt_mem "start" string_or_int_jsont
469469+ ~enc:(fun r -> r.publication.start)
470470+ |> Jsont.Object.opt_mem "end" string_or_int_jsont
471471+ ~enc:(fun r -> r.publication.end_)
472472+ |> Jsont.Object.opt_mem "edition" Jsont.string
473473+ ~enc:(fun r -> r.publication.edition)
474474+ |> Jsont.Object.opt_mem "section" string_or_int_jsont
475475+ ~enc:(fun r -> r.publication.section)
476476+ |> Jsont.Object.opt_mem "status" Cff_enums.Status.jsont
477477+ ~enc:(fun r -> r.publication.status)
478478+ (* Collection fields *)
479479+ |> Jsont.Object.opt_mem "collection-title" Jsont.string
480480+ ~enc:(fun r -> r.collection.collection_title)
481481+ |> Jsont.Object.opt_mem "collection-type" Jsont.string
482482+ ~enc:(fun r -> r.collection.collection_type)
483483+ |> Jsont.Object.opt_mem "collection-doi" Jsont.string
484484+ ~enc:(fun r -> r.collection.collection_doi)
485485+ |> Jsont.Object.opt_mem "volume-title" Jsont.string
486486+ ~enc:(fun r -> r.collection.volume_title)
487487+ |> Jsont.Object.opt_mem "number-volumes" string_or_int_jsont
488488+ ~enc:(fun r -> r.collection.number_volumes)
489489+ (* Date fields *)
490490+ |> Jsont.Object.opt_mem "date-accessed" Cff_date.jsont
491491+ ~enc:(fun r -> r.dates.date_accessed)
492492+ |> Jsont.Object.opt_mem "date-downloaded" Cff_date.jsont
493493+ ~enc:(fun r -> r.dates.date_downloaded)
494494+ |> Jsont.Object.opt_mem "date-published" Cff_date.jsont
495495+ ~enc:(fun r -> r.dates.date_published)
496496+ |> Jsont.Object.opt_mem "date-released" Cff_date.jsont
497497+ ~enc:(fun r -> r.dates.date_released)
498498+ |> Jsont.Object.opt_mem "year" Jsont.int
499499+ ~enc:(fun r -> r.dates.year)
500500+ |> Jsont.Object.opt_mem "year-original" Jsont.int
501501+ ~enc:(fun r -> r.dates.year_original)
502502+ |> Jsont.Object.opt_mem "month" Jsont.int
503503+ ~enc:(fun r -> r.dates.month)
504504+ |> Jsont.Object.opt_mem "issue-date" Jsont.string
505505+ ~enc:(fun r -> r.dates.issue_date)
506506+ (* Identifier fields *)
507507+ |> Jsont.Object.opt_mem "doi" Jsont.string
508508+ ~enc:(fun r -> r.identifiers.doi)
509509+ |> Jsont.Object.opt_mem "url" Jsont.string
510510+ ~enc:(fun r -> r.identifiers.url)
511511+ |> Jsont.Object.opt_mem "repository" Jsont.string
512512+ ~enc:(fun r -> r.identifiers.repository)
513513+ |> Jsont.Object.opt_mem "repository-code" Jsont.string
514514+ ~enc:(fun r -> r.identifiers.repository_code)
515515+ |> Jsont.Object.opt_mem "repository-artifact" Jsont.string
516516+ ~enc:(fun r -> r.identifiers.repository_artifact)
517517+ |> Jsont.Object.opt_mem "isbn" Jsont.string
518518+ ~enc:(fun r -> r.identifiers.isbn)
519519+ |> Jsont.Object.opt_mem "issn" string_or_int_jsont
520520+ ~enc:(fun r -> r.identifiers.issn)
521521+ |> Jsont.Object.opt_mem "pmcid" Jsont.string
522522+ ~enc:(fun r -> r.identifiers.pmcid)
523523+ |> Jsont.Object.opt_mem "nihmsid" Jsont.string
524524+ ~enc:(fun r -> r.identifiers.nihmsid)
525525+ |> Jsont.Object.opt_mem "identifiers" identifiers_list_jsont
526526+ ~enc:(fun r -> r.identifiers.identifiers)
527527+ (* Entity fields *)
528528+ |> Jsont.Object.opt_mem "editors" authors_list_jsont
529529+ ~enc:(fun r -> r.entities.editors)
530530+ |> Jsont.Object.opt_mem "editors-series" authors_list_jsont
531531+ ~enc:(fun r -> r.entities.editors_series)
532532+ |> Jsont.Object.opt_mem "translators" authors_list_jsont
533533+ ~enc:(fun r -> r.entities.translators)
534534+ |> Jsont.Object.opt_mem "recipients" authors_list_jsont
535535+ ~enc:(fun r -> r.entities.recipients)
536536+ |> Jsont.Object.opt_mem "senders" authors_list_jsont
537537+ ~enc:(fun r -> r.entities.senders)
538538+ |> Jsont.Object.opt_mem "contact" authors_list_jsont
539539+ ~enc:(fun r -> r.entities.contact)
540540+ |> Jsont.Object.opt_mem "publisher" Cff_author.Entity.jsont
541541+ ~enc:(fun r -> r.entities.publisher)
542542+ |> Jsont.Object.opt_mem "institution" Cff_author.Entity.jsont
543543+ ~enc:(fun r -> r.entities.institution)
544544+ |> Jsont.Object.opt_mem "conference" Cff_author.Entity.jsont
545545+ ~enc:(fun r -> r.entities.conference)
546546+ |> Jsont.Object.opt_mem "database-provider" Cff_author.Entity.jsont
547547+ ~enc:(fun r -> r.entities.database_provider)
548548+ |> Jsont.Object.opt_mem "location" Cff_author.Entity.jsont
549549+ ~enc:(fun r -> r.entities.location)
550550+ (* Metadata fields *)
551551+ |> Jsont.Object.opt_mem "keywords" string_list_jsont
552552+ ~enc:(fun r -> r.metadata.keywords)
553553+ |> Jsont.Object.opt_mem "languages" string_list_jsont
554554+ ~enc:(fun r -> r.metadata.languages)
555555+ |> Jsont.Object.opt_mem "license" Cff_license.jsont_lenient
556556+ ~enc:(fun r -> r.metadata.license)
557557+ |> Jsont.Object.opt_mem "license-url" Jsont.string
558558+ ~enc:(fun r -> r.metadata.license_url)
559559+ |> Jsont.Object.opt_mem "copyright" Jsont.string
560560+ ~enc:(fun r -> r.metadata.copyright)
561561+ |> Jsont.Object.opt_mem "scope" Jsont.string
562562+ ~enc:(fun r -> r.metadata.scope)
563563+ |> Jsont.Object.opt_mem "notes" Jsont.string
564564+ ~enc:(fun r -> r.metadata.notes)
565565+ (* Technical fields *)
566566+ |> Jsont.Object.opt_mem "commit" Jsont.string
567567+ ~enc:(fun r -> r.technical.commit)
568568+ |> Jsont.Object.opt_mem "version" string_or_int_jsont
569569+ ~enc:(fun r -> r.technical.version)
570570+ |> Jsont.Object.opt_mem "filename" Jsont.string
571571+ ~enc:(fun r -> r.technical.filename)
572572+ |> Jsont.Object.opt_mem "format" Jsont.string
573573+ ~enc:(fun r -> r.technical.format)
574574+ |> Jsont.Object.opt_mem "medium" Jsont.string
575575+ ~enc:(fun r -> r.technical.medium)
576576+ |> Jsont.Object.opt_mem "data-type" Jsont.string
577577+ ~enc:(fun r -> r.technical.data_type)
578578+ |> Jsont.Object.opt_mem "database" Jsont.string
579579+ ~enc:(fun r -> r.technical.database)
580580+ |> Jsont.Object.opt_mem "number" string_or_int_jsont
581581+ ~enc:(fun r -> r.technical.number)
582582+ |> Jsont.Object.opt_mem "patent-states" string_list_jsont
583583+ ~enc:(fun r -> r.technical.patent_states)
584584+ |> Jsont.Object.opt_mem "thesis-type" Jsont.string
585585+ ~enc:(fun r -> r.technical.thesis_type)
586586+ |> Jsont.Object.opt_mem "term" Jsont.string
587587+ ~enc:(fun r -> r.technical.term)
588588+ |> Jsont.Object.opt_mem "entry" Jsont.string
589589+ ~enc:(fun r -> r.technical.entry)
590590+ |> Jsont.Object.opt_mem "department" Jsont.string
591591+ ~enc:(fun r -> r.technical.department)
592592+ |> Jsont.Object.opt_mem "loc-start" string_or_int_jsont
593593+ ~enc:(fun r -> r.technical.loc_start)
594594+ |> Jsont.Object.opt_mem "loc-end" string_or_int_jsont
595595+ ~enc:(fun r -> r.technical.loc_end)
596596+ |> Jsont.Object.skip_unknown
597597+ |> Jsont.Object.finish
+578
project/ocaml-cff/lib/cff_reference.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Bibliographic reference type for CFF.
77+88+ References represent citable works in the [references] and
99+ [preferred-citation] fields of a CFF file. They can describe any
1010+ type of scholarly output: journal articles, books, conference papers,
1111+ software, datasets, theses, patents, and many more.
1212+1313+ {1 Structure}
1414+1515+ CFF references have 60+ possible fields. This module organizes them
1616+ into logical sub-records for easier manipulation:
1717+1818+ - {!Core} - Required fields: type, title, authors
1919+ - {!Publication} - Journal articles: journal, volume, issue, pages
2020+ - {!Collection} - Book chapters, proceedings: collection title, DOI
2121+ - {!Dates} - When the work was published, accessed, etc.
2222+ - {!Identifiers} - DOI, URL, ISBN, ISSN, repository links
2323+ - {!Entities} - Editors, publisher, institution, conference
2424+ - {!Metadata} - Keywords, license, languages, copyright
2525+ - {!Technical} - Software-specific: commit, version, format
2626+2727+ {1 Reference Types}
2828+2929+ The [type] field determines what kind of work is being referenced.
3030+ CFF 1.2.0 supports 40+ types including:
3131+3232+ - Academic: [`Article], [`Book], [`Conference_paper], [`Thesis]
3333+ - Software: [`Software], [`Software_code], [`Software_container]
3434+ - Data: [`Data], [`Database], [`Dataset]
3535+ - Legal: [`Patent], [`Legal_case], [`Statute]
3636+ - Media: [`Video], [`Sound_recording], [`Film_broadcast]
3737+3838+ {1 Example}
3939+4040+ {[
4141+ (* A journal article reference *)
4242+ let article = Cff_reference.make_simple
4343+ ~type_:`Article
4444+ ~title:"The Software Citation Principles"
4545+ ~authors:[
4646+ Cff_author.Person (Cff_author.Person.make
4747+ ~family_names:"Smith"
4848+ ~given_names:"Arfon M."
4949+ ());
5050+ ]
5151+ ~doi:"10.7717/peerj-cs.86"
5252+ ~year:2016
5353+ ~journal:"PeerJ Computer Science"
5454+ ()
5555+5656+ (* A software reference with more details *)
5757+ let core = Cff_reference.Core.make
5858+ ~type_:`Software
5959+ ~title:"NumPy"
6060+ ~authors:[...]
6161+ () in
6262+ let dates = Cff_reference.Dates.make ~year:2020 () in
6363+ let ids = Cff_reference.Identifiers.make
6464+ ~doi:"10.1038/s41586-020-2649-2"
6565+ ~url:"https://numpy.org"
6666+ () in
6767+ let software = Cff_reference.make ~core ~dates ~identifiers:ids ()
6868+ ]}
6969+7070+ {1 Sub-records} *)
7171+7272+(** Core identity fields (required for all references).
7373+7474+ Every reference must have a type, title, and at least one author.
7575+ The type determines what additional fields are relevant. *)
7676+module Core : sig
7777+ type t
7878+7979+ val make :
8080+ type_:Cff_enums.Reference_type.t ->
8181+ title:string ->
8282+ authors:Cff_author.t list ->
8383+ ?abstract:string ->
8484+ ?abbreviation:string ->
8585+ unit -> t
8686+ (** Create a core record.
8787+8888+ @param type_ The reference type (article, book, software, etc.)
8989+ @param title The title of the work
9090+ @param authors List of persons and/or entities *)
9191+9292+ val type_ : t -> Cff_enums.Reference_type.t
9393+ (** The reference type. Determines which other fields are applicable. *)
9494+9595+ val title : t -> string
9696+ (** The title of the referenced work. *)
9797+9898+ val authors : t -> Cff_author.t list
9999+ (** The authors/creators of the work. *)
100100+101101+ val abstract : t -> string option
102102+ (** A description or abstract of the work. *)
103103+104104+ val abbreviation : t -> string option
105105+ (** Abbreviated form of the title (e.g., for journal names). *)
106106+107107+ val pp : Format.formatter -> t -> unit
108108+end
109109+110110+(** Publication metadata for journal articles and periodicals.
111111+112112+ Fields for works published in journals, magazines, or other
113113+ serial publications. Page numbers can be specified as a range
114114+ ([pages]) or as separate [start] and [end_] values. *)
115115+module Publication : sig
116116+ type t
117117+118118+ val empty : t
119119+ (** Empty publication record with all fields as [None]. *)
120120+121121+ val make :
122122+ ?journal:string ->
123123+ ?volume:string ->
124124+ ?issue:string ->
125125+ ?pages:string ->
126126+ ?start:string ->
127127+ ?end_:string ->
128128+ ?edition:string ->
129129+ ?section:string ->
130130+ ?status:Cff_enums.Status.t ->
131131+ unit -> t
132132+133133+ val journal : t -> string option
134134+ (** The name of the journal or magazine. *)
135135+136136+ val volume : t -> string option
137137+ (** The volume number of the journal. *)
138138+139139+ val issue : t -> string option
140140+ (** The issue number within the volume. *)
141141+142142+ val pages : t -> string option
143143+ (** Page range (e.g., ["123-145"]). Alternative to [start]/[end_]. *)
144144+145145+ val start : t -> string option
146146+ (** Starting page number. *)
147147+148148+ val end_ : t -> string option
149149+ (** Ending page number. *)
150150+151151+ val edition : t -> string option
152152+ (** The edition of the work (e.g., ["2nd edition"]). *)
153153+154154+ val section : t -> string option
155155+ (** The section of a work (e.g., newspaper section). *)
156156+157157+ val status : t -> Cff_enums.Status.t option
158158+ (** Publication status: preprint, in-press, submitted, etc. *)
159159+160160+ val is_empty : t -> bool
161161+ (** [true] if all fields are [None]. *)
162162+end
163163+164164+(** Collection metadata for works in edited volumes.
165165+166166+ Used for book chapters, conference proceedings, and other works
167167+ that appear within a larger collection. *)
168168+module Collection : sig
169169+ type t
170170+171171+ val empty : t
172172+173173+ val make :
174174+ ?collection_title:string ->
175175+ ?collection_type:string ->
176176+ ?collection_doi:string ->
177177+ ?volume_title:string ->
178178+ ?number_volumes:string ->
179179+ unit -> t
180180+181181+ val collection_title : t -> string option
182182+ (** Title of the collection (proceedings, book series, etc.). *)
183183+184184+ val collection_type : t -> string option
185185+ (** Type of collection (e.g., ["proceedings"], ["book series"]). *)
186186+187187+ val collection_doi : t -> string option
188188+ (** DOI of the collection itself (not the individual work). *)
189189+190190+ val volume_title : t -> string option
191191+ (** Title of the specific volume within a multi-volume collection. *)
192192+193193+ val number_volumes : t -> string option
194194+ (** Total number of volumes in the collection. *)
195195+196196+ val is_empty : t -> bool
197197+end
198198+199199+(** Date-related fields.
200200+201201+ CFF distinguishes between several date types:
202202+ - {b date-released}: When the software/dataset was released
203203+ - {b date-published}: When the work was formally published
204204+ - {b date-accessed}: When an online resource was last accessed
205205+ - {b date-downloaded}: When a resource was downloaded
206206+207207+ For older works or when only the year is known, use [year] instead
208208+ of a full date. *)
209209+module Dates : sig
210210+ type t
211211+212212+ val empty : t
213213+214214+ val make :
215215+ ?date_accessed:Cff_date.t ->
216216+ ?date_downloaded:Cff_date.t ->
217217+ ?date_published:Cff_date.t ->
218218+ ?date_released:Cff_date.t ->
219219+ ?year:int ->
220220+ ?year_original:int ->
221221+ ?month:int ->
222222+ ?issue_date:string ->
223223+ unit -> t
224224+225225+ val date_accessed : t -> Cff_date.t option
226226+ (** Date when an online resource was accessed for citation. *)
227227+228228+ val date_downloaded : t -> Cff_date.t option
229229+ (** Date when a resource was downloaded. *)
230230+231231+ val date_published : t -> Cff_date.t option
232232+ (** Formal publication date. *)
233233+234234+ val date_released : t -> Cff_date.t option
235235+ (** Release date (typically for software). *)
236236+237237+ val year : t -> int option
238238+ (** Publication year when full date is unknown. *)
239239+240240+ val year_original : t -> int option
241241+ (** Year of original publication (for reprints, translations). *)
242242+243243+ val month : t -> int option
244244+ (** Publication month (1-12) when only month/year is known. *)
245245+246246+ val issue_date : t -> string option
247247+ (** Issue date as a string (for periodicals with specific dates). *)
248248+249249+ val is_empty : t -> bool
250250+end
251251+252252+(** Identifiers and repository links.
253253+254254+ Various identifier schemes for locating and citing works:
255255+ - DOI: Digital Object Identifier (preferred for academic works)
256256+ - URL: Web address
257257+ - ISBN: International Standard Book Number
258258+ - ISSN: International Standard Serial Number (journals)
259259+ - PMCID: PubMed Central ID
260260+ - NIHMSID: NIH Manuscript Submission ID *)
261261+module Identifiers : sig
262262+ type t
263263+264264+ val empty : t
265265+266266+ val make :
267267+ ?doi:string ->
268268+ ?url:string ->
269269+ ?repository:string ->
270270+ ?repository_code:string ->
271271+ ?repository_artifact:string ->
272272+ ?isbn:string ->
273273+ ?issn:string ->
274274+ ?pmcid:string ->
275275+ ?nihmsid:string ->
276276+ ?identifiers:Cff_identifier.t list ->
277277+ unit -> t
278278+279279+ val doi : t -> string option
280280+ (** Digital Object Identifier (e.g., ["10.1234/example"]). *)
281281+282282+ val url : t -> string option
283283+ (** URL where the work can be accessed. *)
284284+285285+ val repository : t -> string option
286286+ (** General repository URL. *)
287287+288288+ val repository_code : t -> string option
289289+ (** Source code repository (GitHub, GitLab, etc.). *)
290290+291291+ val repository_artifact : t -> string option
292292+ (** Built artifact repository (npm, PyPI, Docker Hub, etc.). *)
293293+294294+ val isbn : t -> string option
295295+ (** International Standard Book Number. *)
296296+297297+ val issn : t -> string option
298298+ (** International Standard Serial Number (for journals). *)
299299+300300+ val pmcid : t -> string option
301301+ (** PubMed Central identifier. *)
302302+303303+ val nihmsid : t -> string option
304304+ (** NIH Manuscript Submission System identifier. *)
305305+306306+ val identifiers : t -> Cff_identifier.t list option
307307+ (** Additional typed identifiers (DOI, URL, SWH, other). *)
308308+309309+ val is_empty : t -> bool
310310+end
311311+312312+(** Related entities: editors, publishers, institutions.
313313+314314+ Persons and organizations involved in the work beyond the authors:
315315+ - Editors of collections or journals
316316+ - Publishers and their locations
317317+ - Academic institutions (for theses, reports)
318318+ - Conferences (for proceedings, presentations) *)
319319+module Entities : sig
320320+ type t
321321+322322+ val empty : t
323323+324324+ val make :
325325+ ?editors:Cff_author.t list ->
326326+ ?editors_series:Cff_author.t list ->
327327+ ?translators:Cff_author.t list ->
328328+ ?recipients:Cff_author.t list ->
329329+ ?senders:Cff_author.t list ->
330330+ ?contact:Cff_author.t list ->
331331+ ?publisher:Cff_author.Entity.t ->
332332+ ?institution:Cff_author.Entity.t ->
333333+ ?conference:Cff_author.Entity.t ->
334334+ ?database_provider:Cff_author.Entity.t ->
335335+ ?location:Cff_author.Entity.t ->
336336+ unit -> t
337337+338338+ val editors : t -> Cff_author.t list option
339339+ (** Editors of the work (for edited volumes). *)
340340+341341+ val editors_series : t -> Cff_author.t list option
342342+ (** Series editors (for book series). *)
343343+344344+ val translators : t -> Cff_author.t list option
345345+ (** Translators of the work. *)
346346+347347+ val recipients : t -> Cff_author.t list option
348348+ (** Recipients (for personal communications). *)
349349+350350+ val senders : t -> Cff_author.t list option
351351+ (** Senders (for personal communications). *)
352352+353353+ val contact : t -> Cff_author.t list option
354354+ (** Contact persons for the work. *)
355355+356356+ val publisher : t -> Cff_author.Entity.t option
357357+ (** Publishing organization. *)
358358+359359+ val institution : t -> Cff_author.Entity.t option
360360+ (** Academic/research institution (for theses, reports). *)
361361+362362+ val conference : t -> Cff_author.Entity.t option
363363+ (** Conference where the work was presented. *)
364364+365365+ val database_provider : t -> Cff_author.Entity.t option
366366+ (** Provider of a database (for data references). *)
367367+368368+ val location : t -> Cff_author.Entity.t option
369369+ (** Location entity (city, venue for conferences). *)
370370+371371+ val is_empty : t -> bool
372372+end
373373+374374+(** Descriptive metadata: keywords, license, notes.
375375+376376+ Additional information about the work for discovery and rights. *)
377377+module Metadata : sig
378378+ type t
379379+380380+ val empty : t
381381+382382+ val make :
383383+ ?keywords:string list ->
384384+ ?languages:string list ->
385385+ ?license:Cff_license.t ->
386386+ ?license_url:string ->
387387+ ?copyright:string ->
388388+ ?scope:string ->
389389+ ?notes:string ->
390390+ unit -> t
391391+392392+ val keywords : t -> string list option
393393+ (** Descriptive keywords for the work. *)
394394+395395+ val languages : t -> string list option
396396+ (** Languages the work is available in (ISO 639 codes). *)
397397+398398+ val license : t -> Cff_license.t option
399399+ (** SPDX license identifier(s). *)
400400+401401+ val license_url : t -> string option
402402+ (** URL to license text (for non-SPDX licenses). *)
403403+404404+ val copyright : t -> string option
405405+ (** Copyright statement. *)
406406+407407+ val scope : t -> string option
408408+ (** Scope of the reference (what aspect it covers). *)
409409+410410+ val notes : t -> string option
411411+ (** Additional notes or comments. *)
412412+413413+ val is_empty : t -> bool
414414+end
415415+416416+(** Technical and domain-specific fields.
417417+418418+ Fields for software, data, and specialized reference types:
419419+ - Software: commit hash, version, filename
420420+ - Theses: thesis type, department
421421+ - Data: data type, database, format
422422+ - Patents: patent states
423423+ - Dictionaries/encyclopedias: term, entry *)
424424+module Technical : sig
425425+ type t
426426+427427+ val empty : t
428428+429429+ val make :
430430+ ?commit:string ->
431431+ ?version:string ->
432432+ ?filename:string ->
433433+ ?format:string ->
434434+ ?medium:string ->
435435+ ?data_type:string ->
436436+ ?database:string ->
437437+ ?number:string ->
438438+ ?patent_states:string list ->
439439+ ?thesis_type:string ->
440440+ ?term:string ->
441441+ ?entry:string ->
442442+ ?department:string ->
443443+ ?loc_start:string ->
444444+ ?loc_end:string ->
445445+ unit -> t
446446+447447+ val commit : t -> string option
448448+ (** Git commit hash or VCS revision. *)
449449+450450+ val version : t -> string option
451451+ (** Version string of the software/data. *)
452452+453453+ val filename : t -> string option
454454+ (** Name of the file being referenced. *)
455455+456456+ val format : t -> string option
457457+ (** Format of the work (e.g., ["PDF"], ["HTML"]). *)
458458+459459+ val medium : t -> string option
460460+ (** Physical medium (e.g., ["CD-ROM"], ["print"]). *)
461461+462462+ val data_type : t -> string option
463463+ (** Type of data (for datasets). *)
464464+465465+ val database : t -> string option
466466+ (** Name of the database. *)
467467+468468+ val number : t -> string option
469469+ (** Report/patent/standard number. *)
470470+471471+ val patent_states : t -> string list option
472472+ (** Countries where a patent is held. *)
473473+474474+ val thesis_type : t -> string option
475475+ (** Type of thesis (["PhD"], ["Master's"], etc.). *)
476476+477477+ val term : t -> string option
478478+ (** Dictionary/encyclopedia term being referenced. *)
479479+480480+ val entry : t -> string option
481481+ (** Encyclopedia entry name. *)
482482+483483+ val department : t -> string option
484484+ (** Academic department (for theses). *)
485485+486486+ val loc_start : t -> string option
487487+ (** Starting line/location in source code. *)
488488+489489+ val loc_end : t -> string option
490490+ (** Ending line/location in source code. *)
491491+492492+ val is_empty : t -> bool
493493+end
494494+495495+(** {1 Reference Type} *)
496496+497497+(** The complete reference type combining all sub-records. *)
498498+type t
499499+500500+val make :
501501+ core:Core.t ->
502502+ ?publication:Publication.t ->
503503+ ?collection:Collection.t ->
504504+ ?dates:Dates.t ->
505505+ ?identifiers:Identifiers.t ->
506506+ ?entities:Entities.t ->
507507+ ?metadata:Metadata.t ->
508508+ ?technical:Technical.t ->
509509+ unit -> t
510510+(** Construct a reference from sub-records.
511511+512512+ Only [core] is required; other sub-records default to empty. *)
513513+514514+val make_simple :
515515+ type_:Cff_enums.Reference_type.t ->
516516+ title:string ->
517517+ authors:Cff_author.t list ->
518518+ ?doi:string ->
519519+ ?year:int ->
520520+ ?journal:string ->
521521+ unit -> t
522522+(** Convenience constructor for simple references.
523523+524524+ Creates a reference with just the most common fields. Suitable
525525+ for quick article or software references. *)
526526+527527+(** {2 Sub-record Accessors} *)
528528+529529+val core : t -> Core.t
530530+(** The core identity fields. *)
531531+532532+val publication : t -> Publication.t
533533+(** Publication metadata (journal, volume, pages). *)
534534+535535+val collection : t -> Collection.t
536536+(** Collection metadata (proceedings, book series). *)
537537+538538+val dates : t -> Dates.t
539539+(** Date-related fields. *)
540540+541541+val identifiers : t -> Identifiers.t
542542+(** Identifiers and links. *)
543543+544544+val entities : t -> Entities.t
545545+(** Related entities (editors, publisher). *)
546546+547547+val metadata : t -> Metadata.t
548548+(** Descriptive metadata (keywords, license). *)
549549+550550+val technical : t -> Technical.t
551551+(** Technical fields (commit, version, format). *)
552552+553553+(** {2 Direct Accessors for Common Fields}
554554+555555+ Convenience accessors that delegate to sub-records. *)
556556+557557+val type_ : t -> Cff_enums.Reference_type.t
558558+(** Shortcut for [Core.type_ (core t)]. *)
559559+560560+val title : t -> string
561561+(** Shortcut for [Core.title (core t)]. *)
562562+563563+val authors : t -> Cff_author.t list
564564+(** Shortcut for [Core.authors (core t)]. *)
565565+566566+val doi : t -> string option
567567+(** Shortcut for [Identifiers.doi (identifiers t)]. *)
568568+569569+val year : t -> int option
570570+(** Shortcut for [Dates.year (dates t)]. *)
571571+572572+(** {1 Formatting and Codec} *)
573573+574574+val pp : Format.formatter -> t -> unit
575575+(** Pretty-print a reference in a human-readable format. *)
576576+577577+val jsont : t Jsont.t
578578+(** JSON/YAML codec for serialization. *)
+175
project/ocaml-cff/lib/cff_root.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Root CFF type. *)
77+88+type t = {
99+ cff_version : string;
1010+ message : string;
1111+ title : string;
1212+ authors : Cff_author.t list;
1313+ abstract : string option;
1414+ commit : string option;
1515+ contact : Cff_author.t list option;
1616+ date_released : Cff_date.t option;
1717+ doi : string option;
1818+ identifiers : Cff_identifier.t list option;
1919+ keywords : string list option;
2020+ license : Cff_license.t option;
2121+ license_url : string option;
2222+ preferred_citation : Cff_reference.t option;
2323+ references : Cff_reference.t list option;
2424+ repository : string option;
2525+ repository_artifact : string option;
2626+ repository_code : string option;
2727+ type_ : Cff_enums.Cff_type.t option;
2828+ url : string option;
2929+ version : string option;
3030+}
3131+3232+let make
3333+ ~cff_version
3434+ ~message
3535+ ~title
3636+ ~authors
3737+ ?abstract
3838+ ?commit
3939+ ?contact
4040+ ?date_released
4141+ ?doi
4242+ ?identifiers
4343+ ?keywords
4444+ ?license
4545+ ?license_url
4646+ ?preferred_citation
4747+ ?references
4848+ ?repository
4949+ ?repository_artifact
5050+ ?repository_code
5151+ ?type_
5252+ ?url
5353+ ?version
5454+ () =
5555+ { cff_version; message; title; authors;
5656+ abstract; commit; contact; date_released; doi;
5757+ identifiers; keywords; license; license_url;
5858+ preferred_citation; references; repository;
5959+ repository_artifact; repository_code; type_; url; version }
6060+6161+(* Required field accessors *)
6262+let cff_version t = t.cff_version
6363+let message t = t.message
6464+let title t = t.title
6565+let authors t = t.authors
6666+6767+(* Optional field accessors *)
6868+let abstract t = t.abstract
6969+let commit t = t.commit
7070+let contact t = t.contact
7171+let date_released t = t.date_released
7272+let doi t = t.doi
7373+let identifiers t = t.identifiers
7474+let keywords t = t.keywords
7575+let license t = t.license
7676+let license_url t = t.license_url
7777+let preferred_citation t = t.preferred_citation
7878+let references t = t.references
7979+let repository t = t.repository
8080+let repository_artifact t = t.repository_artifact
8181+let repository_code t = t.repository_code
8282+let type_ t = t.type_
8383+let url t = t.url
8484+let version t = t.version
8585+8686+let make_simple ~title ~authors ?version ?doi ?license () =
8787+ let message = "If you use this software, please cite it using the metadata from this file." in
8888+ make
8989+ ~cff_version:"1.2.0"
9090+ ~message
9191+ ~title
9292+ ~authors
9393+ ?version
9494+ ?doi
9595+ ?license
9696+ ()
9797+9898+let pp ppf t =
9999+ Format.fprintf ppf "@[<v>";
100100+ Format.fprintf ppf "cff-version: %s@," t.cff_version;
101101+ Format.fprintf ppf "title: %s@," t.title;
102102+ Format.fprintf ppf "message: %s@," t.message;
103103+ Format.fprintf ppf "authors:@,";
104104+ List.iter (fun a -> Format.fprintf ppf " - %a@," Cff_author.pp a) t.authors;
105105+ Option.iter (fun v -> Format.fprintf ppf "version: %s@," v) t.version;
106106+ Option.iter (fun v -> Format.fprintf ppf "doi: %s@," v) t.doi;
107107+ Option.iter (fun v -> Format.fprintf ppf "date-released: %a@," Cff_date.pp v) t.date_released;
108108+ Option.iter (fun v -> Format.fprintf ppf "license: %a@," Cff_license.pp v) t.license;
109109+ Option.iter (fun v -> Format.fprintf ppf "url: %s@," v) t.url;
110110+ Option.iter (fun v -> Format.fprintf ppf "repository: %s@," v) t.repository;
111111+ Option.iter (fun v -> Format.fprintf ppf "repository-code: %s@," v) t.repository_code;
112112+ Option.iter (fun v -> Format.fprintf ppf "abstract: %s@," v) t.abstract;
113113+ Option.iter (fun v -> Format.fprintf ppf "commit: %s@," v) t.commit;
114114+ Option.iter (fun v -> Format.fprintf ppf "type: %a@," Cff_enums.Cff_type.pp v) t.type_;
115115+ Option.iter (fun kws ->
116116+ Format.fprintf ppf "keywords:@,";
117117+ List.iter (fun k -> Format.fprintf ppf " - %s@," k) kws
118118+ ) t.keywords;
119119+ Option.iter (fun ids ->
120120+ Format.fprintf ppf "identifiers:@,";
121121+ List.iter (fun id -> Format.fprintf ppf " - %a@," Cff_identifier.pp id) ids
122122+ ) t.identifiers;
123123+ Option.iter (fun contacts ->
124124+ Format.fprintf ppf "contact:@,";
125125+ List.iter (fun c -> Format.fprintf ppf " - %a@," Cff_author.pp c) contacts
126126+ ) t.contact;
127127+ Option.iter (fun refs ->
128128+ Format.fprintf ppf "references:@,";
129129+ List.iter (fun r -> Format.fprintf ppf " - %a@," Cff_reference.pp r) refs
130130+ ) t.references;
131131+ Option.iter (fun pc ->
132132+ Format.fprintf ppf "preferred-citation:@, %a@," Cff_reference.pp pc
133133+ ) t.preferred_citation;
134134+ Format.fprintf ppf "@]"
135135+136136+let jsont =
137137+ let open Jsont in
138138+ let array_to_list arr = Stdlib.Array.to_list arr in
139139+ let array_of_list lst = Stdlib.Array.of_list lst in
140140+ let authors_jsont = array Cff_author.jsont |> map ~dec:array_to_list ~enc:array_of_list in
141141+ let identifiers_jsont = array Cff_identifier.jsont |> map ~dec:array_to_list ~enc:array_of_list in
142142+ let references_jsont = array Cff_reference.jsont |> map ~dec:array_to_list ~enc:array_of_list in
143143+ let keywords_jsont = array string |> map ~dec:array_to_list ~enc:array_of_list in
144144+ Object.map ~kind:"CFF"
145145+ (fun cff_version message title authors abstract commit contact
146146+ date_released doi identifiers keywords license license_url
147147+ preferred_citation references repository repository_artifact
148148+ repository_code type_ url version ->
149149+ { cff_version; message; title; authors;
150150+ abstract; commit; contact; date_released; doi;
151151+ identifiers; keywords; license; license_url;
152152+ preferred_citation; references; repository;
153153+ repository_artifact; repository_code; type_; url; version })
154154+ |> Object.mem "cff-version" string ~enc:(fun t -> t.cff_version)
155155+ |> Object.mem "message" string ~enc:(fun t -> t.message)
156156+ |> Object.mem "title" string ~enc:(fun t -> t.title)
157157+ |> Object.mem "authors" authors_jsont ~enc:(fun t -> t.authors)
158158+ |> Object.opt_mem "abstract" string ~enc:(fun t -> t.abstract)
159159+ |> Object.opt_mem "commit" string ~enc:(fun t -> t.commit)
160160+ |> Object.opt_mem "contact" authors_jsont ~enc:(fun t -> t.contact)
161161+ |> Object.opt_mem "date-released" Cff_date.jsont ~enc:(fun t -> t.date_released)
162162+ |> Object.opt_mem "doi" string ~enc:(fun t -> t.doi)
163163+ |> Object.opt_mem "identifiers" identifiers_jsont ~enc:(fun t -> t.identifiers)
164164+ |> Object.opt_mem "keywords" keywords_jsont ~enc:(fun t -> t.keywords)
165165+ |> Object.opt_mem "license" Cff_license.jsont_lenient ~enc:(fun t -> t.license)
166166+ |> Object.opt_mem "license-url" string ~enc:(fun t -> t.license_url)
167167+ |> Object.opt_mem "preferred-citation" Cff_reference.jsont ~enc:(fun t -> t.preferred_citation)
168168+ |> Object.opt_mem "references" references_jsont ~enc:(fun t -> t.references)
169169+ |> Object.opt_mem "repository" string ~enc:(fun t -> t.repository)
170170+ |> Object.opt_mem "repository-artifact" string ~enc:(fun t -> t.repository_artifact)
171171+ |> Object.opt_mem "repository-code" string ~enc:(fun t -> t.repository_code)
172172+ |> Object.opt_mem "type" Cff_enums.Cff_type.jsont ~enc:(fun t -> t.type_)
173173+ |> Object.opt_mem "url" string ~enc:(fun t -> t.url)
174174+ |> Object.opt_mem "version" string ~enc:(fun t -> t.version)
175175+ |> Object.finish
+249
project/ocaml-cff/lib/cff_root.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 The ocaml-cff programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Root CFF type representing a complete [CITATION.cff] file.
77+88+ A [CITATION.cff] file is the standard way to provide citation metadata
99+ for research software and datasets. This module defines the root type
1010+ containing all top-level fields from the CFF 1.2.0 specification.
1111+1212+ {2 Required Fields}
1313+1414+ Every valid CFF file must include:
1515+ - {!cff_version}: Schema version (["1.2.0"])
1616+ - {!message}: Instructions for citing the work
1717+ - {!title}: Name of the software or dataset
1818+ - {!authors}: List of persons and/or entities
1919+2020+ {2 Common Optional Fields}
2121+2222+ - {!version}: Software version string
2323+ - {!doi}: Digital Object Identifier
2424+ - {!date_released}: Publication/release date
2525+ - {!license}: SPDX license identifier(s)
2626+ - {!keywords}: Descriptive keywords
2727+ - {!abstract}: Description of the work
2828+2929+ {2 Citation Redirection}
3030+3131+ The {!preferred_citation} field allows redirecting citations to
3232+ a related work (e.g., a journal article describing the software).
3333+ The {!references} field lists works that the software cites or
3434+ depends upon.
3535+3636+ {2 Example}
3737+3838+ {[
3939+ let cff = Cff_root.make
4040+ ~cff_version:"1.2.0"
4141+ ~message:"If you use this software, please cite it as below."
4242+ ~title:"My Research Software"
4343+ ~authors:[Cff_author.Person (Cff_author.Person.make
4444+ ~family_names:"Smith"
4545+ ~given_names:"Jane"
4646+ ())]
4747+ ~version:"1.0.0"
4848+ ~doi:"10.5281/zenodo.1234567"
4949+ ~date_released:(2024, 1, 15)
5050+ ~license:(Cff_license.single "MIT")
5151+ ()
5252+ ]} *)
5353+5454+(** The abstract type representing a complete CFF document. *)
5555+type t
5656+5757+(** {1 Construction} *)
5858+5959+val make :
6060+ cff_version:string ->
6161+ message:string ->
6262+ title:string ->
6363+ authors:Cff_author.t list ->
6464+ ?abstract:string ->
6565+ ?commit:string ->
6666+ ?contact:Cff_author.t list ->
6767+ ?date_released:Cff_date.t ->
6868+ ?doi:string ->
6969+ ?identifiers:Cff_identifier.t list ->
7070+ ?keywords:string list ->
7171+ ?license:Cff_license.t ->
7272+ ?license_url:string ->
7373+ ?preferred_citation:Cff_reference.t ->
7474+ ?references:Cff_reference.t list ->
7575+ ?repository:string ->
7676+ ?repository_artifact:string ->
7777+ ?repository_code:string ->
7878+ ?type_:Cff_enums.Cff_type.t ->
7979+ ?url:string ->
8080+ ?version:string ->
8181+ unit -> t
8282+(** [make ~cff_version ~message ~title ~authors ...] constructs a CFF value.
8383+8484+ @param cff_version The CFF schema version, typically ["1.2.0"]
8585+ @param message Instructions for users on how to cite the work
8686+ @param title The name of the software or dataset
8787+ @param authors List of persons and/or entities who created the work *)
8888+8989+(** {1 Required Fields} *)
9090+9191+val cff_version : t -> string
9292+(** The CFF schema version that this file adheres to.
9393+9494+ For CFF 1.2.0 files, this should be ["1.2.0"]. The version determines
9595+ which keys are valid and how they should be interpreted. *)
9696+9797+val message : t -> string
9898+(** A message to readers explaining how to cite the work.
9999+100100+ Common examples:
101101+ - ["If you use this software, please cite it using the metadata from this file."]
102102+ - ["Please cite this software using the metadata from 'preferred-citation'."]
103103+104104+ The message should guide users toward the preferred citation method. *)
105105+106106+val title : t -> string
107107+(** The name of the software or dataset.
108108+109109+ This is the title that should appear in citations. For software, it's
110110+ typically the project name; for datasets, the dataset title. *)
111111+112112+val authors : t -> Cff_author.t list
113113+(** The creators of the software or dataset.
114114+115115+ Authors can be persons (individuals) or entities (organizations).
116116+ At least one author is required for a valid CFF file. The order
117117+ typically reflects contribution significance. *)
118118+119119+(** {1 Optional Fields} *)
120120+121121+val abstract : t -> string option
122122+(** A description of the software or dataset.
123123+124124+ Provides context about what the work does, its purpose, and scope. *)
125125+126126+val commit : t -> string option
127127+(** The commit hash or revision number of the software version.
128128+129129+ Useful for precise version identification beyond semantic versioning.
130130+ Example: ["1ff847d81f29c45a3a1a5ce73d38e45c2f319bba"] *)
131131+132132+val contact : t -> Cff_author.t list option
133133+(** Contact persons or entities for the software or dataset.
134134+135135+ May differ from authors; useful when the primary contact is a
136136+ project maintainer rather than the original author. *)
137137+138138+val date_released : t -> Cff_date.t option
139139+(** The date when the software or dataset was released.
140140+141141+ Format is [(year, month, day)], corresponding to ISO 8601 [YYYY-MM-DD]. *)
142142+143143+val doi : t -> string option
144144+(** The Digital Object Identifier for the software or dataset.
145145+146146+ DOIs provide persistent, citable identifiers. This is a shorthand
147147+ for a single DOI; use {!identifiers} for multiple DOIs or other
148148+ identifier types. Example: ["10.5281/zenodo.1234567"] *)
149149+150150+val identifiers : t -> Cff_identifier.t list option
151151+(** Additional identifiers beyond the primary DOI.
152152+153153+ Each identifier has a type (DOI, URL, SWH, other), value, and
154154+ optional description. Useful for versioned DOIs, Software Heritage
155155+ identifiers, or repository URLs. *)
156156+157157+val keywords : t -> string list option
158158+(** Descriptive keywords for the work.
159159+160160+ Help with discoverability and categorization. Example:
161161+ [["machine learning"; "image processing"; "python"]] *)
162162+163163+val license : t -> Cff_license.t option
164164+(** The SPDX license identifier(s) for the work.
165165+166166+ Uses {{:https://spdx.org/licenses/}SPDX identifiers}. Multiple
167167+ licenses imply an OR relationship (user may choose any).
168168+ Example: ["MIT"], ["Apache-2.0"], or [["GPL-3.0-only"; "MIT"]]. *)
169169+170170+val license_url : t -> string option
171171+(** URL to the license text for non-standard licenses.
172172+173173+ Only needed for licenses not in the SPDX list. Standard SPDX
174174+ licenses have well-known URLs. *)
175175+176176+val preferred_citation : t -> Cff_reference.t option
177177+(** A reference to cite instead of the software itself.
178178+179179+ Used for "credit redirection" when authors prefer citation of
180180+ a related publication (e.g., a methods paper) over the software.
181181+ Note: Software citation principles recommend citing software
182182+ directly; use this field judiciously. *)
183183+184184+val references : t -> Cff_reference.t list option
185185+(** Works that this software cites or depends upon.
186186+187187+ Functions like a bibliography, listing dependencies, foundational
188188+ works, or related publications. Each reference includes full
189189+ bibliographic metadata. *)
190190+191191+val repository : t -> string option
192192+(** URL to the repository where the software is developed.
193193+194194+ Typically a version control system URL. For source code repositories,
195195+ prefer {!repository_code}. *)
196196+197197+val repository_artifact : t -> string option
198198+(** URL to the built/compiled artifact repository.
199199+200200+ For binary distributions, package registries (npm, PyPI, CRAN),
201201+ or container registries. *)
202202+203203+val repository_code : t -> string option
204204+(** URL to the source code repository.
205205+206206+ Typically a GitHub, GitLab, or similar URL where the source
207207+ code is publicly accessible. *)
208208+209209+val type_ : t -> Cff_enums.Cff_type.t option
210210+(** The type of work: [`Software] (default) or [`Dataset].
211211+212212+ Most CFF files describe software; use [`Dataset] for data packages. *)
213213+214214+val url : t -> string option
215215+(** The URL of the software or dataset homepage.
216216+217217+ A general landing page, documentation site, or project website. *)
218218+219219+val version : t -> string option
220220+(** The version string of the software or dataset.
221221+222222+ Can be any version format: semantic versioning (["1.2.3"]),
223223+ date-based (["2024.01"]), or other schemes. *)
224224+225225+(** {1 Convenience Constructors} *)
226226+227227+val make_simple :
228228+ title:string ->
229229+ authors:Cff_author.t list ->
230230+ ?version:string ->
231231+ ?doi:string ->
232232+ ?license:Cff_license.t ->
233233+ unit -> t
234234+(** Create a minimal CFF with sensible defaults.
235235+236236+ Uses [cff_version = "1.2.0"] and the standard message:
237237+ ["If you use this software, please cite it using the metadata from this file."]
238238+239239+ This is the quickest way to create a valid CFF for simple projects. *)
240240+241241+(** {1 Formatting and Codec} *)
242242+243243+val pp : Format.formatter -> t -> unit
244244+(** Pretty-print a CFF value in a human-readable YAML-like format. *)
245245+246246+val jsont : t Jsont.t
247247+(** JSON/YAML codec for serialization and deserialization.
248248+249249+ Used internally by the YAML codec functions. *)
···11+---
22+name: Idea for a schema/format enhancement
33+about: I have an idea for how to improve the Citation File Format schema
44+title: ''
55+labels: 'enhancement'
66+assignees: ''
77+88+---
···11+name: Tool idea
22+description: I have an idea for or need specific tooling for CITATION.cff files
33+labels: ['help wanted', 'tooling']
44+body:
55+ - type: markdown
66+ attributes:
77+ value: Please have a look at the [section on tooling for CITATION.cff files](https://github.com/citation-file-format/citation-file-format#tools-to-work-with-citationcff-files-wrench) before submitting this issue.
88+ - type: textarea
99+ attributes:
1010+ label: Who needs the new tool?
1111+ description: Describe the type of user that would need the new tool.
1212+ placeholder: |
1313+ Examples:
1414+1515+ * Researchers
1616+ * A software registry
1717+ validations:
1818+ required: true
1919+ - type: textarea
2020+ attributes:
2121+ label: What should the new tool enable users to do?
2222+ description: Describe a goal that users could achieve with the new tool. You can start with "I want to ..." or similar.
2323+ placeholder: |
2424+ Example:
2525+2626+ I want to convert the metadata in CITATION.cff files to BibTeX.
2727+ validations:
2828+ required: true
2929+ - type: textarea
3030+ attributes:
3131+ label: What benefit would the new tool add?
3232+ description: Describe the benefit the tool will give users.
3333+ placeholder: |
3434+ Example:
3535+3636+ Users will be able to use the metadata from CITATION.cff files to cite software in papers they write in LaTeX.
3737+ validations:
3838+ required: true
3939+ - type: textarea
4040+ attributes:
4141+ label: Implementation suggestions
4242+ description: Do you have a specific technology or programming language in mind for the implementation of the new tool? If so, please describe why.
4343+ placeholder: |
4444+ Examples:
4545+4646+ * This could be a website hosted by the software registries that want to use this functionality.
4747+ * This could be a Python package with a command-line interface.
4848+ validations:
4949+ required: false
5050+ - type: dropdown
5151+ attributes:
5252+ label: Can you help?
5353+ description: Indicate whether you will be able to work on the implementation yourself.
5454+ options:
5555+ - 'Yes'
5656+ - 'No'
5757+ validations:
5858+ required: true
···11+---
22+name: Validation issue
33+about: My CITATION.cff file is not valid and I need help
44+title: ''
55+labels:
66+ - 'help wanted'
77+ - validation
88+assignees: ''
99+1010+---
1111+1212+<!--
1313+Please have a look at the section on validating your CITATION.cff file before submitting this issue: https://github.com/citation-file-format/citation-file-format#validation-heavy_check_mark.
1414+-->
1515+1616+<!-- Don't delete the following note! -->
1717+Please note that issues with the validity of single CITATION.cff files may take some time to be picked up by the Citation File Format maintainers themselves. Therefore, if you are reading this issue and know how to validate `CITATION.cff` files, please help out if you can!
1818+1919+## Invalid `CITATION.cff` file
2020+2121+```yaml
2222+Paste your invalid CITATION.cff file here!
2323+```
2424+2525+## Context
2626+2727+- Please describe what you want to do (create a valid `CITATION.cff` file, show the citation information in the sidebar of your GitHub repository, ...)
···11+**Related issues**
22+33+Refs: #ISSUE_NUMBER
44+55+(For autoclosure of issues when PR is merged use `Fixes #<issue-number>` syntax)
66+77+**Describe the changes made in this pull request**
88+99+**Review checklist**
1010+1111+- [ ] Please check if the pull request is against the correct branch
1212+(format/schema/semantic documentation changes: `develop`; typos, meta files, etc.: `main`)
1313+- [ ] Please check if all changes are recorded in `CHANGELOG.md` and adapt if necessary.
1414+- [ ] Please run tests locally.
1515+<!--
1616+CONTRIBUTORS: Please replace <do other things> in the snippet below
1717+with something that reviewers should do to test and review your contribution!
1818+-->
1919+```bash
2020+cd $(mktemp -d --tmpdir cff.XXXXXX)
2121+git clone https://github.com/citation-file-format/citation-file-format .
2222+git checkout <branch>
2323+python3 -m venv env
2424+source env/bin/activate
2525+pip install --upgrade pip wheel setuptools
2626+pip install -r requirements.txt
2727+pytest -v
2828+<do other things>
2929+```
3030+<!--
3131+CONTRIBUTORS: Please replace `<do other things>` in the checklist item below
3232+with something that reviewers should do additionally
3333+to test and review your contribution!
3434+-->
3535+- [ ] `<do other things>`
···11+{
22+ "_comment": "Markdown Link Checker configuration, see https://github.com/gaurav-nelson/github-action-markdown-link-check and https://github.com/tcort/markdown-link-check",
33+ "ignorePatterns": [{
44+ "pattern": "^http://localhost"
55+ }, {
66+ "pattern": "^https://localhost"
77+ }, {
88+ "_comment": "local links are not checked reliably, skipping helps avoid false sense of security",
99+ "pattern": "^#"
1010+ }, {
1111+ "_comment": "Resolves fine, target may be behind a proxy server that doesn't allow this kind of automated access",
1212+ "pattern": "^https://doi\\.org/10\\.25490/a97f-egyk"
1313+ }, {
1414+ "_comment": "Resolves fine, target may be behind a proxy server that doesn't allow this kind of automated access",
1515+ "pattern": "^https://www\\.esciencecenter\\.nl"
1616+ }],
1717+ "retryOn429": true,
1818+ "fallbackRetryDelay": "30s"
1919+}
···11+# Changelog
22+33+All notable changes to this project will be documented in this file.
44+55+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77+88+## [Unreleased]
99+1010+### Changed
1111+1212+- Update example testing to use cffconvert 2.0.0 (or newer) validation
1313+- Update Python versions run in CI to remove end-of-life versions and add newer releases
1414+- Replace real ORCID with example ORCID from https://orcid.org/1234-5678-9101-1121
1515+- Provide contact details for the Code of Conduct Committee in the CoC document
1616+- Speed up the test suite
1717+1818+## [1.2.0] - 2021-05-31
1919+2020+### Added
2121+2222+- introduced `preferred-citation`
2323+- root document now has `type:` `software` or `dataset`
2424+- `identifiers` have gained an optional key `description`
2525+- added regex validation for identifiers of type `swh`
2626+- `description`s and `examples` added to schema
2727+- schema has more checks for empty strings or empty arrays (e.g. `authors`, `abstract`, `keywords`)
2828+2929+### Changed
3030+3131+- switched from YAML schema to JSON schema
3232+- `issue`, `number`, `version`, `post-code`, `section` are more lenient now with type union `str|number`
3333+- `loc-start`, `loc-end`, `start`, `end`, `number-volumes`, `volume`, `pages`, `year`, `year-original` are more lenient now with type union `int|str`
3434+- `month` is more lenient now with type union `int[1-12]|enum["1"-"12"]`
3535+- `version`, `date-released` no longer required elements of a minimal CFF
3636+- list of valid license SPDX codes updated to version 2021-05-14
3737+- `language` more lenient (for performance)
3838+- regex for `url` simplified for easier maintenance
3939+- url regex now also allows for `sftp`
4040+- regex for `isbn` simplified and changed for easier maintenance
4141+- dates are now (only) strings of `format` `date` & `pattern` YYYY-MM-DD
4242+4343+### Fixed
4444+4545+- `references.term` was added
4646+4747+## [1.1.0] - 2021-05-31
4848+4949+### Added
5050+5151+- `identifiers` field that accepts `doi`, `swh` (short for "Software Heritage"), `url` and `other` identifiers
5252+- `alias` for *person* objects to accept aliases such as user/screen names, etc.
5353+5454+### Changed
5555+5656+- Make `given-names` and `family-names` non-required in *person* objects
5757+5858+### Fixed
5959+6060+- Note in schema comment that DOI can include slashes
6161+6262+## [1.0.3-4] - 2019-11-05
6363+6464+### Added
6565+6666+- Link to concept DOI rather than single version DOI (https://github.com/citation-file-format/citation-file-format.github.io/commit/4a55d34fc7fb760a0930c2f934f97d4cbe1eb52f)
6767+- Note arbitrary order of keys (https://github.com/citation-file-format/citation-file-format.github.io/commit/aa63008257011c86f120a6498abaa9c08e04ce0d)
6868+- Add Neil Chue Hong to authors
6969+- Add Jurriaan Spaaks to authors
7070+7171+### Fixed
7272+7373+- `orcid` accepts URLs (https://github.com/citation-file-format/citation-file-format/issues/43)
7474+- Fix some typos in the specifications
7575+- Improve rendering of documentation
7676+7777+## [1.0.2] - 2017-12-20
7878+7979+### Fixed
8080+8181+- Fix examples in versions 1.0.0 and 1.0.1 (https://github.com/citation-file-format/citation-file-format/issues/41)
8282+- Indicate required keys for software metadata in the specifications document (https://github.com/citation-file-format/citation-file-format/issues/42)
8383+- Add known bugs to specifications versions 1.0.0 and 1.0.1 (https://github.com/citation-file-format/citation-file-format.github.io/commit/de801adaf86496d4d1948f90e61a699de208508f)
8484+8585+## [1.0.1] - 2017-12-18
8686+8787+### Fixed
8888+8989+- `patent-states` takes a sequence of strings, not a single string (https://github.com/citation-file-format/citation-file-format/issues/39)
9090+- `senders` also accepts entity objects (https://github.com/citation-file-format/citation-file-format/issues/40)
9191+9292+## [1.0.0] - 2017-12-12
9393+9494+### Changed
9595+9696+- Initial full release of the Citation File Format specifications
9797+9898+## [0.9-RC1] - 2017-10-06
9999+100100+### Added
101101+102102+- Initial pre-release of the specifications for the Citation File Format
103103+104104+[unreleased]: https://github.com/citation-file-format/citation-file-format/compare/1.2.0...HEAD
105105+[1.2.0]: https://doi.org/10.5281/zenodo.5171937
106106+[1.1.0]: https://doi.org/10.5281/zenodo.4813122
107107+[1.0.3-4]: https://doi.org/10.5281/zenodo.3515946
108108+[1.0.2]: https://doi.org/10.5281/zenodo.1120256
109109+[1.0.1]: https://doi.org/10.5281/zenodo.1117789
110110+[1.0.0]: https://doi.org/10.5281/zenodo.1108269
111111+[0.9-RC1]: https://doi.org/10.5281/zenodo.1003150
···11+# Code of Conduct
22+33+In the interest of fostering an open and welcoming environment, we as
44+contributors and maintainers pledge to making participation in our project and
55+our community a harassment-free experience for everyone, regardless of gender,
66+gender identity and expression, age, sexual identity and orientation,
77+disability, physical appearance, body size, race, ethnicity, religion (or lack
88+thereof), technology choices, level of experience, or other group status.
99+1010+## Standards
1111+1212+Examples of behavior that contributes to creating a positive environment
1313+include:
1414+1515+- Using welcoming and inclusive language
1616+- Being respectful of differing viewpoints and experiences
1717+- Gracefully accepting constructive criticism
1818+- Focusing on what is best for the community
1919+- Showing empathy towards other community members
2020+2121+Examples of unacceptable behavior by participants include:
2222+2323+- The use of sexualized language or imagery and unwelcome sexual attention or
2424+ advances
2525+- Trolling, insulting/derogatory comments, and personal or political attacks
2626+- Public or private harassment
2727+- Publishing others' private information, such as a physical or electronic
2828+ address, without explicit permission
2929+- Other conduct which could reasonably be considered inappropriate in a
3030+ professional setting
3131+3232+## Responsibilities
3333+3434+Project maintainers are responsible for clarifying the standards of acceptable
3535+behavior and are expected to take appropriate and fair corrective action in
3636+response to any instances of unacceptable behavior.
3737+3838+Project maintainers have the right and responsibility to remove, edit, or
3939+reject comments, commits, code, wiki edits, issues, and other contributions
4040+that are not aligned to this Code of Conduct, or to ban temporarily or
4141+permanently any contributor for other behaviors that they deem inappropriate,
4242+threatening, offensive, or harmful.
4343+4444+## Scope
4545+4646+This Code of Conduct applies both within project spaces and in public spaces
4747+when an individual is representing the project or its community. Examples of
4848+representing a project or community include using an official project e-mail
4949+address, posting via an official social media account, or acting as an appointed
5050+representative at an online or offline event. Representation of a project may be
5151+further defined and clarified by project maintainers.
5252+5353+## Enforcement
5454+5555+Instances of abusive, harassing, or otherwise unacceptable behavior may be
5656+reported by contacting the [Code of Conduct Committee](#code-of-conduct-committee). All
5757+complaints will be reviewed and investigated and will result in a response that
5858+is deemed necessary and appropriate to the circumstances. The project team is
5959+obligated to maintain confidentiality with regard to the reporter of an incident.
6060+Further details of specific enforcement policies may be posted separately.
6161+6262+Project maintainers who do not follow or enforce the Code of Conduct in good
6363+faith may face temporary or permanent repercussions as determined by other
6464+members of the project's maintenance team.
6565+6666+### Code of Conduct Committee
6767+6868+The current Code of Conduct Committee consists of:
6969+7070+- Neil Chue Hong ([N.ChueHong@epcc.ed.ac.uk](mailto:N.ChueHong@epcc.ed.ac.uk))
7171+- Robert Haines ([robert.haines@manchester.ac.uk](mailto:robert.haines@manchester.ac.uk))
7272+7373+You may contact members individually or collectively.
7474+Note that if you contact members individually, other members may still be involved in resolving the issue.
7575+If members are subject of a report themselves, they will not be involved in the resolution of the issue as members of the Code of Conduct Committee, but may be addressed by other members as reported subjects to resolve the issue.
7676+7777+## Attribution
7878+7979+This Code of Conduct is adapted from the
8080+[Contributor Covenant](https://www.contributor-covenant.org/),
8181+version 1.4, available at
8282+https://www.contributor-covenant.org/version/1/4/code-of-conduct.html,
8383+and the [WSSSPE5.1](http://wssspe.researchcomputing.org.uk/wssspe5-1/)
8484+Code of Conduct, available at
8585+http://wssspe.researchcomputing.org.uk/wssspe5-1/wssspe5-1-code-of-conduct/.
···11+# Introduction
22+33+**Thank you** for considering to contribute to the **Citation File Format (CFF) and its schema**!
44+If you intend to contribute to another part of the Citation File Format project,
55+for example a specific software package for working with CFF files,
66+please contribute to the respective repository ([list of repositories in the `citation-file-format` GitHub organization](https://github.com/orgs/citation-file-format/repositories)).
77+88+**Please follow these guidelines.** Their purpose is to make both contributing and accepting contributions easier for all parties involved.
99+1010+There are many **ways to contribute**, e.g.:
1111+1212+- Tell a friend or colleague about the Citation File Format, or tweet about it
1313+- Write blog posts, tutorials, etc. about the Citation File Format
1414+- Review the format and its schema and documentation
1515+- Improve wording in any prose output, including the specifications
1616+- Create a new, better version of the schema and specifications
1717+- Improve automated tests, continuous integration, documentation, etc.
1818+1919+# Ground Rules
2020+2121+Your contribution to CFF is valued, and it should be an enjoyable experience.
2222+To ensure this, there is the CFF
2323+[Code of Conduct](https://github.com/citation-file-format/citation-file-format/blob/master/CODE_OF_CONDUCT.md), which you are required to follow.
2424+2525+Please always start any contribution that will change the contents of this repository by [creating a new issue](https://github.com/citation-file-format/citation-file-format/issues/new/choose).
2626+This way,
2727+2828+- you can make sure that you don't invest your valuable time in something that may not be merged;
2929+- we can make sure that your contribution is something that will improve CFF,
3030+is in scope,
3131+and aligns with the roadmap for the Citation File Format.
3232+3333+# Your First Contribution
3434+3535+If you are unsure where to begin with your contribution to CFF, have a look at the
3636+[open issues in this repository](https://github.com/citation-file-format/citation-file-format/issues),
3737+and see if you can identify one that you would like to work on.
3838+3939+If you have never contributed to an open source project, you may find this tutorial helpful:
4040+[How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
4141+4242+# Getting started
4343+4444+This is the workflow for contributions to this repository:
4545+4646+1. [Create a new issue](https://github.com/citation-file-format/citation-file-format/issues/new/choose)
4747+to discuss the changes you want to make with the maintainers and community
4848+2. Fork the repository
4949+3. Create a branch in your fork of the repository:
5050+ - If you want to make changes to the format and its schema: checkout `develop` and create your branch from it.
5151+ - If you want to make changes that don't affect the format and its schema, like fixing typos or formatting: checkout `main` and create your branch from it.
5252+ - When in doubt which branch to fork from, ask in the issue you have created.
5353+4. Make changes in the new branch in your fork
5454+5. Take note of the [code of conduct](https://github.com/citation-file-format/citation-file-format/blob/main/CODE_OF_CONDUCT.md)
5555+6. Create a pull request
5656+7. Address any comments that have come up during review
5757+8. If and when your pull request has been merged, you can delete your branch (or the whole forked repository)
5858+5959+This workflow is loosely based on GitHub flow, and you can find more information in the [GitHub flow documentation](https://docs.github.com/en/get-started/quickstart/github-flow).
6060+6161+## Working with examples and tests
6262+6363+There is a collection of test `CITATION.cff` files in the `examples` directory.
6464+It may be a good idea to add a test if you modify the specification,
6565+or if you discover a part of the specification that is not covered
6666+by tests. If you modify anything in the `tests` or `examples` directory, it is
6767+advised to run these tests locally on your computer prior to submitting
6868+a pull request. However, if that's not possible, you still can submit
6969+the pull request and later check the status of the tests for your
7070+pull requests on GitHub. Please see [`examples/README.md`](examples/README.md) file for further
7171+details about tests and instructions how to run them locally.
7272+7373+<!--
7474+TODO Include a link to README.dev.md here once it exists!
7575+See https://github.com/citation-file-format/citation-file-format/issues/301
7676+-->
7777+7878+# How to submit an issue
7979+8080+If you find a security vulnerability anywhere, do NOT open an issue. Email *cff-
8181+security /at\ sdruskat \dot/ net* instead.
8282+8383+This repository uses issue templates, so that when
8484+you create a new issue, you can simply fill it in and everything that is
8585+needed to work on your issue will be there.
8686+8787+If no issue template exists for your specific case, please use the template for "Any other issue".
8888+8989+# How to suggest a feature or enhancement
9090+9191+See [How to submit an issue](#how-to-submit-an-issue).
9292+9393+# FAQ
9494+9595+- **These guidelines do not address aspect XYZ! What should I do now?**
9696+9797+Please [submit an issue](https://github.com/citation-file-format/citation-file-format/issues/new/choose),
9898+asking for clarification and addition to the guidelines.
···11+Creative Commons Attribution 4.0 International Public License
22+33+=======================================================================
44+55+Creative Commons Corporation ("Creative Commons") is not a law firm and
66+does not provide legal services or legal advice. Distribution of
77+Creative Commons public licenses does not create a lawyer-client or
88+other relationship. Creative Commons makes its licenses and related
99+information available on an "as-is" basis. Creative Commons gives no
1010+warranties regarding its licenses, any material licensed under their
1111+terms and conditions, or any related information. Creative Commons
1212+disclaims all liability for damages resulting from their use to the
1313+fullest extent possible.
1414+1515+Using Creative Commons Public Licenses
1616+1717+Creative Commons public licenses provide a standard set of terms and
1818+conditions that creators and other rights holders may use to share
1919+original works of authorship and other material subject to copyright
2020+and certain other rights specified in the public license below. The
2121+following considerations are for informational purposes only, are not
2222+exhaustive, and do not form part of our licenses.
2323+2424+ Considerations for licensors: Our public licenses are
2525+ intended for use by those authorized to give the public
2626+ permission to use material in ways otherwise restricted by
2727+ copyright and certain other rights. Our licenses are
2828+ irrevocable. Licensors should read and understand the terms
2929+ and conditions of the license they choose before applying it.
3030+ Licensors should also secure all rights necessary before
3131+ applying our licenses so that the public can reuse the
3232+ material as expected. Licensors should clearly mark any
3333+ material not subject to the license. This includes other CC-
3434+ licensed material, or material used under an exception or
3535+ limitation to copyright. More considerations for licensors:
3636+ wiki.creativecommons.org/Considerations_for_licensors
3737+3838+ Considerations for the public: By using one of our public
3939+ licenses, a licensor grants the public permission to use the
4040+ licensed material under specified terms and conditions. If
4141+ the licensor's permission is not necessary for any reason--for
4242+ example, because of any applicable exception or limitation to
4343+ copyright--then that use is not regulated by the license. Our
4444+ licenses grant only permissions under copyright and certain
4545+ other rights that a licensor has authority to grant. Use of
4646+ the licensed material may still be restricted for other
4747+ reasons, including because others have copyright or other
4848+ rights in the material. A licensor may make special requests,
4949+ such as asking that all changes be marked or described.
5050+ Although not required by our licenses, you are encouraged to
5151+ respect those requests where reasonable. More_considerations
5252+ for the public:
5353+ wiki.creativecommons.org/Considerations_for_licensees
5454+5555+=======================================================================
5656+5757+Creative Commons Attribution 4.0 International Public License
5858+5959+By exercising the Licensed Rights (defined below), You accept and agree
6060+to be bound by the terms and conditions of this Creative Commons
6161+Attribution 4.0 International Public License ("Public License"). To the
6262+extent this Public License may be interpreted as a contract, You are
6363+granted the Licensed Rights in consideration of Your acceptance of
6464+these terms and conditions, and the Licensor grants You such rights in
6565+consideration of benefits the Licensor receives from making the
6666+Licensed Material available under these terms and conditions.
6767+6868+6969+Section 1 -- Definitions.
7070+7171+ a. Adapted Material means material subject to Copyright and Similar
7272+ Rights that is derived from or based upon the Licensed Material
7373+ and in which the Licensed Material is translated, altered,
7474+ arranged, transformed, or otherwise modified in a manner requiring
7575+ permission under the Copyright and Similar Rights held by the
7676+ Licensor. For purposes of this Public License, where the Licensed
7777+ Material is a musical work, performance, or sound recording,
7878+ Adapted Material is always produced where the Licensed Material is
7979+ synched in timed relation with a moving image.
8080+8181+ b. Adapter's License means the license You apply to Your Copyright
8282+ and Similar Rights in Your contributions to Adapted Material in
8383+ accordance with the terms and conditions of this Public License.
8484+8585+ c. Copyright and Similar Rights means copyright and/or similar rights
8686+ closely related to copyright including, without limitation,
8787+ performance, broadcast, sound recording, and Sui Generis Database
8888+ Rights, without regard to how the rights are labeled or
8989+ categorized. For purposes of this Public License, the rights
9090+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
9191+ Rights.
9292+9393+ d. Effective Technological Measures means those measures that, in the
9494+ absence of proper authority, may not be circumvented under laws
9595+ fulfilling obligations under Article 11 of the WIPO Copyright
9696+ Treaty adopted on December 20, 1996, and/or similar international
9797+ agreements.
9898+9999+ e. Exceptions and Limitations means fair use, fair dealing, and/or
100100+ any other exception or limitation to Copyright and Similar Rights
101101+ that applies to Your use of the Licensed Material.
102102+103103+ f. Licensed Material means the artistic or literary work, database,
104104+ or other material to which the Licensor applied this Public
105105+ License.
106106+107107+ g. Licensed Rights means the rights granted to You subject to the
108108+ terms and conditions of this Public License, which are limited to
109109+ all Copyright and Similar Rights that apply to Your use of the
110110+ Licensed Material and that the Licensor has authority to license.
111111+112112+ h. Licensor means the individual(s) or entity(ies) granting rights
113113+ under this Public License.
114114+115115+ i. Share means to provide material to the public by any means or
116116+ process that requires permission under the Licensed Rights, such
117117+ as reproduction, public display, public performance, distribution,
118118+ dissemination, communication, or importation, and to make material
119119+ available to the public including in ways that members of the
120120+ public may access the material from a place and at a time
121121+ individually chosen by them.
122122+123123+ j. Sui Generis Database Rights means rights other than copyright
124124+ resulting from Directive 96/9/EC of the European Parliament and of
125125+ the Council of 11 March 1996 on the legal protection of databases,
126126+ as amended and/or succeeded, as well as other essentially
127127+ equivalent rights anywhere in the world.
128128+129129+ k. You means the individual or entity exercising the Licensed Rights
130130+ under this Public License. Your has a corresponding meaning.
131131+132132+133133+Section 2 -- Scope.
134134+135135+ a. License grant.
136136+137137+ 1. Subject to the terms and conditions of this Public License,
138138+ the Licensor hereby grants You a worldwide, royalty-free,
139139+ non-sublicensable, non-exclusive, irrevocable license to
140140+ exercise the Licensed Rights in the Licensed Material to:
141141+142142+ a. reproduce and Share the Licensed Material, in whole or
143143+ in part; and
144144+145145+ b. produce, reproduce, and Share Adapted Material.
146146+147147+ 2. Exceptions and Limitations. For the avoidance of doubt, where
148148+ Exceptions and Limitations apply to Your use, this Public
149149+ License does not apply, and You do not need to comply with
150150+ its terms and conditions.
151151+152152+ 3. Term. The term of this Public License is specified in Section
153153+ 6(a).
154154+155155+ 4. Media and formats; technical modifications allowed. The
156156+ Licensor authorizes You to exercise the Licensed Rights in
157157+ all media and formats whether now known or hereafter created,
158158+ and to make technical modifications necessary to do so. The
159159+ Licensor waives and/or agrees not to assert any right or
160160+ authority to forbid You from making technical modifications
161161+ necessary to exercise the Licensed Rights, including
162162+ technical modifications necessary to circumvent Effective
163163+ Technological Measures. For purposes of this Public License,
164164+ simply making modifications authorized by this Section 2(a)
165165+ (4) never produces Adapted Material.
166166+167167+ 5. Downstream recipients.
168168+169169+ a. Offer from the Licensor -- Licensed Material. Every
170170+ recipient of the Licensed Material automatically
171171+ receives an offer from the Licensor to exercise the
172172+ Licensed Rights under the terms and conditions of this
173173+ Public License.
174174+175175+ b. No downstream restrictions. You may not offer or impose
176176+ any additional or different terms or conditions on, or
177177+ apply any Effective Technological Measures to, the
178178+ Licensed Material if doing so restricts exercise of the
179179+ Licensed Rights by any recipient of the Licensed
180180+ Material.
181181+182182+ 6. No endorsement. Nothing in this Public License constitutes or
183183+ may be construed as permission to assert or imply that You
184184+ are, or that Your use of the Licensed Material is, connected
185185+ with, or sponsored, endorsed, or granted official status by,
186186+ the Licensor or others designated to receive attribution as
187187+ provided in Section 3(a)(1)(A)(i).
188188+189189+ b. Other rights.
190190+191191+ 1. Moral rights, such as the right of integrity, are not
192192+ licensed under this Public License, nor are publicity,
193193+ privacy, and/or other similar personality rights; however, to
194194+ the extent possible, the Licensor waives and/or agrees not to
195195+ assert any such rights held by the Licensor to the limited
196196+ extent necessary to allow You to exercise the Licensed
197197+ Rights, but not otherwise.
198198+199199+ 2. Patent and trademark rights are not licensed under this
200200+ Public License.
201201+202202+ 3. To the extent possible, the Licensor waives any right to
203203+ collect royalties from You for the exercise of the Licensed
204204+ Rights, whether directly or through a collecting society
205205+ under any voluntary or waivable statutory or compulsory
206206+ licensing scheme. In all other cases the Licensor expressly
207207+ reserves any right to collect such royalties.
208208+209209+210210+Section 3 -- License Conditions.
211211+212212+Your exercise of the Licensed Rights is expressly made subject to the
213213+following conditions.
214214+215215+ a. Attribution.
216216+217217+ 1. If You Share the Licensed Material (including in modified
218218+ form), You must:
219219+220220+ a. retain the following if it is supplied by the Licensor
221221+ with the Licensed Material:
222222+223223+ i. identification of the creator(s) of the Licensed
224224+ Material and any others designated to receive
225225+ attribution, in any reasonable manner requested by
226226+ the Licensor (including by pseudonym if
227227+ designated);
228228+229229+ ii. a copyright notice;
230230+231231+ iii. a notice that refers to this Public License;
232232+233233+ iv. a notice that refers to the disclaimer of
234234+ warranties;
235235+236236+ v. a URI or hyperlink to the Licensed Material to the
237237+ extent reasonably practicable;
238238+239239+ b. indicate if You modified the Licensed Material and
240240+ retain an indication of any previous modifications; and
241241+242242+ c. indicate the Licensed Material is licensed under this
243243+ Public License, and include the text of, or the URI or
244244+ hyperlink to, this Public License.
245245+246246+ 2. You may satisfy the conditions in Section 3(a)(1) in any
247247+ reasonable manner based on the medium, means, and context in
248248+ which You Share the Licensed Material. For example, it may be
249249+ reasonable to satisfy the conditions by providing a URI or
250250+ hyperlink to a resource that includes the required
251251+ information.
252252+253253+ 3. If requested by the Licensor, You must remove any of the
254254+ information required by Section 3(a)(1)(A) to the extent
255255+ reasonably practicable.
256256+257257+ 4. If You Share Adapted Material You produce, the Adapter's
258258+ License You apply must not prevent recipients of the Adapted
259259+ Material from complying with this Public License.
260260+261261+262262+Section 4 -- Sui Generis Database Rights.
263263+264264+Where the Licensed Rights include Sui Generis Database Rights that
265265+apply to Your use of the Licensed Material:
266266+267267+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
268268+ to extract, reuse, reproduce, and Share all or a substantial
269269+ portion of the contents of the database;
270270+271271+ b. if You include all or a substantial portion of the database
272272+ contents in a database in which You have Sui Generis Database
273273+ Rights, then the database in which You have Sui Generis Database
274274+ Rights (but not its individual contents) is Adapted Material; and
275275+276276+ c. You must comply with the conditions in Section 3(a) if You Share
277277+ all or a substantial portion of the contents of the database.
278278+279279+For the avoidance of doubt, this Section 4 supplements and does not
280280+replace Your obligations under this Public License where the Licensed
281281+Rights include other Copyright and Similar Rights.
282282+283283+284284+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
285285+286286+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
287287+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
288288+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
289289+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
290290+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
291291+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
292292+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
293293+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
294294+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
295295+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
296296+297297+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
298298+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
299299+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
300300+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
301301+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
302302+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
303303+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
304304+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
305305+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
306306+307307+ c. The disclaimer of warranties and limitation of liability provided
308308+ above shall be interpreted in a manner that, to the extent
309309+ possible, most closely approximates an absolute disclaimer and
310310+ waiver of all liability.
311311+312312+313313+Section 6 -- Term and Termination.
314314+315315+ a. This Public License applies for the term of the Copyright and
316316+ Similar Rights licensed here. However, if You fail to comply with
317317+ this Public License, then Your rights under this Public License
318318+ terminate automatically.
319319+320320+ b. Where Your right to use the Licensed Material has terminated under
321321+ Section 6(a), it reinstates:
322322+323323+ 1. automatically as of the date the violation is cured, provided
324324+ it is cured within 30 days of Your discovery of the
325325+ violation; or
326326+327327+ 2. upon express reinstatement by the Licensor.
328328+329329+ For the avoidance of doubt, this Section 6(b) does not affect any
330330+ right the Licensor may have to seek remedies for Your violations
331331+ of this Public License.
332332+333333+ c. For the avoidance of doubt, the Licensor may also offer the
334334+ Licensed Material under separate terms or conditions or stop
335335+ distributing the Licensed Material at any time; however, doing so
336336+ will not terminate this Public License.
337337+338338+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
339339+ License.
340340+341341+342342+Section 7 -- Other Terms and Conditions.
343343+344344+ a. The Licensor shall not be bound by any additional or different
345345+ terms or conditions communicated by You unless expressly agreed.
346346+347347+ b. Any arrangements, understandings, or agreements regarding the
348348+ Licensed Material not stated herein are separate from and
349349+ independent of the terms and conditions of this Public License.
350350+351351+352352+Section 8 -- Interpretation.
353353+354354+ a. For the avoidance of doubt, this Public License does not, and
355355+ shall not be interpreted to, reduce, limit, restrict, or impose
356356+ conditions on any use of the Licensed Material that could lawfully
357357+ be made without permission under this Public License.
358358+359359+ b. To the extent possible, if any provision of this Public License is
360360+ deemed unenforceable, it shall be automatically reformed to the
361361+ minimum extent necessary to make it enforceable. If the provision
362362+ cannot be reformed, it shall be severed from this Public License
363363+ without affecting the enforceability of the remaining terms and
364364+ conditions.
365365+366366+ c. No term or condition of this Public License will be waived and no
367367+ failure to comply consented to unless expressly agreed to by the
368368+ Licensor.
369369+370370+ d. Nothing in this Public License constitutes or may be interpreted
371371+ as a limitation upon, or waiver of, any privileges and immunities
372372+ that apply to the Licensor or You, including from the legal
373373+ processes of any jurisdiction or authority.
374374+375375+376376+=======================================================================
377377+378378+Creative Commons is not a party to its public
379379+licenses. Notwithstanding, Creative Commons may elect to apply one of
380380+its public licenses to material it publishes and in those instances
381381+will be considered the “Licensor.” The text of the Creative Commons
382382+public licenses is dedicated to the public domain under the CC0 Public
383383+Domain Dedication. Except for the limited purpose of indicating that
384384+material is shared under a Creative Commons public license or as
385385+otherwise permitted by the Creative Commons policies published at
386386+creativecommons.org/policies, Creative Commons does not authorize the
387387+use of the trademark "Creative Commons" or any other trademark or logo
388388+of Creative Commons without its prior written consent including,
389389+without limitation, in connection with any unauthorized modifications
390390+to any of its public licenses or any other arrangements,
391391+understandings, or agreements concerning use of licensed material. For
392392+the avoidance of doubt, this paragraph does not form part of the
393393+public licenses.
394394+395395+Creative Commons may be contacted at creativecommons.org.
···11+cff-version: 1.0.3
22+message: If you use this software, please cite the software itself and the blog post.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: blog
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Implement a 100% accuracy syntax parser for all languages? No probs!"
1717+ date-published: 2017-09-23
1818+ url: https://hu-berlin.de/blogs/jdoe/2017/09/23/if-only
1919+ institution:
2020+ name: "Humboldt-Universität zu Berlin"
2121+ city: Berlin
2222+ country: DE
···11+cff-version: 1.0.3
22+message: "If you use MRT for your research, please cite the following book."
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: book
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "The future of syntax parsing"
1717+ year: 2017
1818+ publisher:
1919+ name: Far Out Publications
2020+ city: Bielefeld
2121+ medium: print
···11+cff-version: 1.0.3
22+message: If you use this software, please cite the software and the paper.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: conference-paper
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Ultimate-accuracy syntax parsing with My Research Tool"
1717+ year: 2017
1818+ collection-title: "Proceedings of the 1st Conference on Wishful Thinking"
1919+ collection-doi: 10.5281/zenodo.123456
2020+ editors:
2121+ - family-names: Kirk
2222+ given-names: James T.
2323+ conference:
2424+ name: 1st Conference on Wishful Thinking
2525+ location: Spock's Inn Hotel and Bar
2626+ address: 123 Main St
2727+ city: Bielefeld
2828+ region: Jarvis Island
2929+ post-code: "12345"
3030+ country: UM
3131+ date-start: 2017-04-01
3232+ date-end: 2017-04-01
3333+ start: 42
3434+ end: 45
3535+ doi: 10.5281/zenodo.1234
···11+cff-version: 1.0.3
22+message: If you use this software, please cite it as below.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: edited-work
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Ultimate-accuracy parsing in practice"
1717+ year: 2017
1818+ publisher:
1919+ name: Far Out Publications
2020+ city: Bielefeld
2121+ country: DE
···11+# software with reference of type edited-work
22+33+Note that the editors of the edited work must be specified under the `authors`
44+key. Specific citation styles may or may not attach a suffix to the authors,
55+such as ", eds." or similar.
···11+# Software with a doi (expanded)
22+33+Same as [../software-with-a-doi/README.md](../software-with-a-doi/README.md), but with additional properties.
···11+cff-version: 1.0.3
22+message: If you use this software, please cite it as below.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
···11+# Software with a DOI
22+33+Note that [Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 12 recommend
44+55+> [...] the use of DOIs as the unique identifier due to their common usage and
66+acceptance, particularly as they are the standard for other digital products
77+such as publications.
88+99+Furthermore, DOIs should point to a "unique, specific software version"
1010+([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 12). Also it is recommended ([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 13) that:
1111+1212+> the [DOI] should resolve to a persistent landing page that contains metadata
1313+and a link to the software itself, rather than directly to the source code files,
1414+repository, or executable.
1515+1616+Therefore, a minimal `CITATION.cff` file in such a case would look similar to [CITATION.cff](CITATION.cff).
···11+cff-version: 1.0.3
22+message: If you use My Research Tool, please cite both the software and the outline paper.
33+authors:
44+ - family-names: Doe
55+ given-names: Jane
66+ - family-names: Bielefeld
77+ name-particle: von
88+ given-names: Arthur
99+ - family-names: McAuthor
1010+ given-names: Juniper
1111+ name-suffix: Jr.
1212+title: My Research Tool
1313+version: 1.0.4
1414+doi: 10.5281/zenodo.1234
1515+date-released: 2017-12-18
1616+references:
1717+ - type: article
1818+ scope: Cite this paper if you want to reference the general concepts of MRT.
1919+ authors:
2020+ - family-names: Doe
2121+ given-names: Jane
2222+ - family-names: Bielefeld
2323+ name-particle: von
2424+ given-names: Arthur
2525+ title: "My Research Tool: A 100% accuracy syntax parser for all languages"
2626+ year: 2099
2727+ journal: Journal of Hard Science Fiction
2828+ volume: 42
2929+ issue: "13"
3030+ doi: 10.9999/hardscifi-lang.42132
···11+# Software with a further reference
22+33+Where authors wish to encourage citation of an outline paper with citation
44+of their software, we recommend the use of [reference keys](#references-optional)
55+to highlight the existence of further references.
···11+cff-version: 1.0.3
22+message:
33+ If you dare use this commercial, closed-source, strangely versioned
44+ software in your research, please at least cite it as below.
55+authors:
66+ - family-names: Vader
77+ name-suffix: né Skywalker
88+ given-names: 'Anakin "Darth"'
99+title: Opaquity
1010+version: opq-1234-XZVF-ACME-RLY
1111+date-released: 2017-02-28
1212+url: http://www.opaquity.com
1313+contact:
1414+ - name: Dark Side Software
1515+ address: DS-1 Orbital Battle Station, near Scarif
1616+ email: father@imperial-empire.com
1717+ tel: +850 (0)123-45-666
···11+For software without a DOI, it is recommended that "the metadata should still
22+provide information on how to access the specific software, but this may be a
33+company’s product number or a link to a website that allows the software be
44+purchased." [Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 13. Furthermore, "if the version number and
55+release date are not available, the download date can be used. Similarly, the
66+contact name/email is an alternative to the location/repository."
77+([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 7).
88+99+Hence, for closed-source software without a DOI for which the version number
1010+and release date cannot be determined, a `CITATION.cff` file could look like
1111+[./CITATION.cff](./CITATION.cff).
···11+We recognize that there are certain situations where it may not be possible to
22+follow the recommended best-practice. For example, if (1) the software authors
33+did not register a DOI and/or release a specific version, or (2) the version of
44+the software used does not match what is available to cite. In those cases,
55+falling back on a combination of the repository URL and version number/commit
66+hash would be an appropriate way to cite the software used ([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 12).
···11+cff-version: 1.1.0
22+message: If you use this software, please cite it as below.
33+authors:
44+ - alias: githubuser
55+title: My Research Tool
66+version: 1.0.4
77+date-released: 2017-12-18
···11+# YAML 1.2
22+# Metadata for citation of this software according to the CFF format (https://citation-file-format.github.io/)
33+cff-version: 1.1.0
44+message: If you use this software, please cite it using these metadata.
55+title: 'ANNIS'
66+doi: 10.5281/zenodo.2563138
77+authors:
88+- given-names: Dummy
99+ family-names: User
1010+ affiliation: Humboldt-Universität zu Berlin
1111+ orcid: https://orcid.org/0000-1234-5678-9101
1212+version: annis-3.6.0
1313+date-released: 2019-02-12
1414+repository-code: https://github.com/korpling/ANNIS
1515+license: Apache-2.0
1616+references:
1717+ - type: article
1818+ title: "ANNIS3: A new architecture for generic corpus query and visualization"
1919+ year: 2016
2020+ doi: 10.1093/llc/fqu057
2121+ authors:
2222+ - family-names: Dummy
2323+ given-names: User
2424+ orcid: https://orcid.org/0000-1234-5678-9101
2525+ affiliation: Humboldt-Universität zu Berlin
2626+ - family-names: Other
2727+ given-names: User
2828+ affiliation: Georgetown University
2929+ journal: Digital Scholarship in the Humanities
3030+ volume: 31
3131+ issue: "1"
3232+ issn: 2055-7671
···11+cff-version: 1.1.0
22+message: If you use this software, please cite it as below.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+title: My Research Tool
77+version: 1.0.4
88+date-released: 2017-12-18
99+identifiers:
1010+ - type: "other"
1111+ value: "This is an (identified) birthday pony!"
1212+ - type: "swh"
1313+ value: "swh:1:rel:99f6850374dc6597af01bd0ee1d3fc0699301b9f"
···11+cff-version: 1.1.0
22+message: If you use this software, please cite the software itself and the blog post.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: blog
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Implement a 100% accuracy syntax parser for all languages? No probs!"
1717+ date-published: 2017-09-23
1818+ url: https://hu-berlin.de/blogs/jdoe/2017/09/23/if-only
1919+ institution:
2020+ name: "Humboldt-Universität zu Berlin"
2121+ city: Berlin
2222+ country: DE
···11+cff-version: 1.1.0
22+message: "If you use MRT for your research, please cite the following book."
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: book
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "The future of syntax parsing"
1717+ year: 2017
1818+ publisher:
1919+ name: Far Out Publications
2020+ city: Bielefeld
2121+ medium: print
···11+cff-version: 1.1.0
22+message: If you use this software, please cite the software and the paper.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: conference-paper
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Ultimate-accuracy syntax parsing with My Research Tool"
1717+ year: 2017
1818+ collection-title: "Proceedings of the 1st Conference on Wishful Thinking"
1919+ collection-doi: 10.5281/zenodo.123456
2020+ editors:
2121+ - family-names: Kirk
2222+ given-names: James T.
2323+ conference:
2424+ name: 1st Conference on Wishful Thinking
2525+ location: Spock's Inn Hotel and Bar
2626+ address: 123 Main St
2727+ city: Bielefeld
2828+ region: Jarvis Island
2929+ post-code: "12345"
3030+ country: UM
3131+ date-start: 2017-04-01
3232+ date-end: 2017-04-01
3333+ start: 42
3434+ end: 45
3535+ doi: 10.5281/zenodo.1234
···11+cff-version: 1.1.0
22+message: If you use this software, please cite it as below.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: edited-work
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Ultimate-accuracy parsing in practice"
1717+ year: 2017
1818+ publisher:
1919+ name: Far Out Publications
2020+ city: Bielefeld
2121+ country: DE
···11+cff-version: 1.1.0
22+message: If you use this software, please cite it as below.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
···11+cff-version: 1.1.0
22+message: If you use My Research Tool, please cite both the software and the outline paper.
33+authors:
44+ - family-names: Doe
55+ given-names: Jane
66+ - family-names: Bielefeld
77+ name-particle: von
88+ given-names: Arthur
99+ - family-names: McAuthor
1010+ given-names: Juniper
1111+ name-suffix: Jr.
1212+title: My Research Tool
1313+version: 1.0.4
1414+doi: 10.5281/zenodo.1234
1515+date-released: 2017-12-18
1616+references:
1717+ - type: article
1818+ scope: Cite this paper if you want to reference the general concepts of MRT.
1919+ authors:
2020+ - family-names: Doe
2121+ given-names: Jane
2222+ - family-names: Bielefeld
2323+ name-particle: von
2424+ given-names: Arthur
2525+ title: "My Research Tool: A 100% accuracy syntax parser for all languages"
2626+ year: 2099
2727+ journal: Journal of Hard Science Fiction
2828+ volume: 42
2929+ issue: "13"
3030+ doi: 10.9999/hardscifi-lang.42132
···11+cff-version: 1.1.0
22+message:
33+ If you dare use this commercial, closed-source, strangely versioned
44+ software in your research, please at least cite it as below.
55+authors:
66+ - family-names: Vader
77+ name-suffix: né Skywalker
88+ given-names: 'Anakin "Darth"'
99+title: Opaquity
1010+version: opq-1234-XZVF-ACME-RLY
1111+date-released: 2017-02-28
1212+url: http://www.opaquity.com
1313+contact:
1414+ - name: Dark Side Software
1515+ address: DS-1 Orbital Battle Station, near Scarif
1616+ email: father@imperial-empire.com
1717+ tel: +850 (0)123-45-666
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
22+33+- Updated `cff-version` to `"1.2.0"`
44+55+Fails because
66+- `date-released` is not in YYYY-MM-DD format
77+- `pages` should be an integer describing the number of pages
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
···11+cff-version: 1.2.0
22+message: If you use this software, please cite both the software repository and the JOSS paper.
33+authors:
44+ - family-names: Morgan
55+ given-names: Benjamin J.
66+ orcid: https://orcid.org/0000-0002-3056-8233
77+title: bsym
88+version: 1.1.0
99+doi: 10.5281/zenodo.596912
1010+repository-code: https://github.com/bjmorgan/bsym
1111+license: MIT
1212+references:
1313+ - type: article
1414+ authors:
1515+ - family-names: Morgan
1616+ given-names: Benjamin J.
1717+ title: "bsym: A basic symmetry module"
1818+ year: 2017
1919+ journal: Journal of Open Source Software
2020+ volume: 2
2121+ issue: "16"
2222+ doi: 10.21105/joss.00370
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
22+33+- Updated `cff-version` to `"1.2.0"`
44+- Updated `version` to be a string
55+
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
22+33+- Updated `cff-version` to `"1.2.0"`
44+- Updated `date-released` to comply with `YYYY-MM-DD` format
55+- Updated `issue` to be a string
66+- Replaced `pages` with `start` and `end`
···11+# A minimal CFF file with only the required fields included.
22+33+cff-version: 1.2.0
44+message: If you use this software in your work, please cite it using the following metadata
55+title: Ruby CFF Library
66+authors:
77+- family-names: Haines
88+ given-names: Robert
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files, with some changes.
···11+cff-version: 1.2.0
22+message: If you use this software, please cite the software itself and the blog post.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: blog
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Implement a 100% accuracy syntax parser for all languages? No probs!"
1717+ date-published: 2017-09-23
1818+ url: https://hu-berlin.de/blogs/jdoe/2017/09/23/if-only
1919+ institution:
2020+ name: "Humboldt-Universität zu Berlin"
2121+ city: Berlin
2222+ country: DE
···11+cff-version: 1.2.0
22+message: "If you use MRT for your research, please cite the following book."
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: book
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "The future of syntax parsing"
1717+ year: 2017
1818+ publisher:
1919+ name: Far Out Publications
2020+ city: Bielefeld
2121+ medium: print
···11+cff-version: 1.2.0
22+message: If you use this software, please cite the software and the paper.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: conference-paper
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Ultimate-accuracy syntax parsing with My Research Tool"
1717+ year: 2017
1818+ collection-title: "Proceedings of the 1st Conference on Wishful Thinking"
1919+ collection-doi: 10.5281/zenodo.123456
2020+ editors:
2121+ - family-names: Kirk
2222+ given-names: James T.
2323+ conference:
2424+ name: 1st Conference on Wishful Thinking
2525+ location: Spock's Inn Hotel and Bar
2626+ address: 123 Main St
2727+ city: Bielefeld
2828+ region: Jarvis Island
2929+ post-code: "12345"
3030+ country: UM
3131+ date-start: 2017-04-01
3232+ date-end: 2017-04-01
3333+ start: 42
3434+ end: 45
3535+ doi: 10.5281/zenodo.1234
···11+cff-version: 1.2.0
22+message: If you use this software, please cite it as below.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
1111+references:
1212+ - type: edited-work
1313+ authors:
1414+ - family-names: Doe
1515+ given-names: Jane
1616+ title: "Ultimate-accuracy parsing in practice"
1717+ year: 2017
1818+ publisher:
1919+ name: Far Out Publications
2020+ city: Bielefeld
2121+ country: DE
···11+# software with reference of type edited-work
22+33+Note that the editors of the edited work must be specified under the `authors`
44+key. Specific citation styles may or may not attach a suffix to the authors,
55+such as ", eds." or similar.
···11+# An incomplete CFF file
22+33+cff-version: 1.2.0
44+message: If you use this software in your work, please cite it using the following metadata
55+title: Ruby CFF Library
66+authors:
77+- family-names: Haines
88+ given-names: Robert
99+ affiliation: The University of Manchester, UK
1010+keywords:
1111+- ruby
1212+- credit
1313+- citation
1414+version: 0.4.0
1515+date-released: 2018-07-22
1616+license: Apache-2.0
1717+repository-artifact: https://rubygems.org/gems/cff
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
···11+cff-version: 1.2.0
22+message: "If you use this software, please cite it as below."
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: "My Research Software"
88+version: 2.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
···11+# Software with a doi (expanded)
22+33+Same as [../software-with-a-doi/README.md](../software-with-a-doi/README.md), but with additional properties.
···11+cff-version: 1.2.0
22+message: If you use this software, please cite it as below.
33+authors:
44+ - family-names: Druskat
55+ given-names: Stephan
66+ orcid: https://orcid.org/0000-0003-4925-7248
77+title: My Research Tool
88+version: 1.0.4
99+doi: 10.5281/zenodo.1234
1010+date-released: 2017-12-18
···11+# Software with a DOI
22+33+Note that [Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 12 recommend
44+55+> [...] the use of DOIs as the unique identifier due to their common usage and
66+acceptance, particularly as they are the standard for other digital products
77+such as publications.
88+99+Furthermore, DOIs should point to a "unique, specific software version"
1010+([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 12). Also it is recommended ([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 13) that:
1111+1212+> the [DOI] should resolve to a persistent landing page that contains metadata
1313+and a link to the software itself, rather than directly to the source code files,
1414+repository, or executable.
1515+1616+Therefore, a minimal `CITATION.cff` file in such a case would look similar to [CITATION.cff](CITATION.cff).
···11+cff-version: 1.2.0
22+message: If you use My Research Tool, please cite both the software and the outline paper.
33+authors:
44+ - family-names: Doe
55+ given-names: Jane
66+ - family-names: Bielefeld
77+ name-particle: von
88+ given-names: Arthur
99+ - family-names: McAuthor
1010+ given-names: Juniper
1111+ name-suffix: Jr.
1212+title: My Research Tool
1313+version: 1.0.4
1414+doi: 10.5281/zenodo.1234
1515+date-released: 2017-12-18
1616+references:
1717+ - type: article
1818+ scope: Cite this paper if you want to reference the general concepts of MRT.
1919+ authors:
2020+ - family-names: Doe
2121+ given-names: Jane
2222+ - family-names: Bielefeld
2323+ name-particle: von
2424+ given-names: Arthur
2525+ title: "My Research Tool: A 100% accuracy syntax parser for all languages"
2626+ year: 2099
2727+ journal: Journal of Hard Science Fiction
2828+ volume: 42
2929+ issue: "13"
3030+ doi: 10.9999/hardscifi-lang.42132
···11+# Software with a further reference
22+33+Where authors wish to encourage citation of an outline paper with citation
44+of their software, we recommend the use of [reference keys](#references-optional)
55+to highlight the existence of further references.
···11+cff-version: 1.2.0
22+message:
33+ If you dare use this commercial, closed-source, strangely versioned
44+ software in your research, please at least cite it as below.
55+authors:
66+ - family-names: Vader
77+ name-suffix: né Skywalker
88+ given-names: 'Anakin "Darth"'
99+title: Opaquity
1010+version: opq-1234-XZVF-ACME-RLY
1111+date-released: 2017-02-28
1212+url: http://www.opaquity.com
1313+contact:
1414+ - name: Dark Side Software
1515+ address: DS-1 Orbital Battle Station, near Scarif
1616+ email: father@imperial-empire.com
1717+ tel: +850 (0)123-45-666
···11+For software without a DOI, it is recommended that "the metadata should still
22+provide information on how to access the specific software, but this may be a
33+company’s product number or a link to a website that allows the software be
44+purchased." [Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 13. Furthermore, "if the version number and
55+release date are not available, the download date can be used. Similarly, the
66+contact name/email is an alternative to the location/repository."
77+([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 7).
88+99+Hence, for closed-source software without a DOI for which the version number
1010+and release date cannot be determined, a `CITATION.cff` file could look like
1111+[./CITATION.cff](./CITATION.cff).
···11+We recognize that there are certain situations where it may not be possible to
22+follow the recommended best-practice. For example, if (1) the software authors
33+did not register a DOI and/or release a specific version, or (2) the version of
44+the software used does not match what is available to cite. In those cases,
55+falling back on a combination of the repository URL and version number/commit
66+hash would be an appropriate way to cite the software used ([Smith et al., 2016](https://doi.org/10.7717/peerj-cs.86), p. 12).
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files, with minor changes:
22+33+1. updated `cff-version` to "1.2.0"
44+1. made `version` a string
55+
···11+# YAML 1.2
22+# Metadata for citation of this software according to the CFF format (https://citation-file-format.github.io/)
33+cff-version: 1.2.0
44+message: If you use this software, please cite it as below.
55+title: Cloud related adaptors for Xenon
66+authors:
77+- given-names: Stefan
88+ family-names: Verhoeven
99+ affiliation: Nederlands eScience Center
1010+ orcid: https://orcid.org/0000-0002-5821-2060
1111+- given-names: Jason
1212+ family-names: Maassen
1313+ affiliation: Netherlands eScience Center
1414+- given-names: Atze
1515+ family-names: van der Ploeg
1616+ affiliation: Netherlands eScience Center
1717+version: 3.0.2
1818+doi: 10.5281/zenodo.3245389
1919+date-released: '2019-08-07'
2020+repository-code: https://github.com/xenon-middleware/xenon-adaptors-cloud
2121+license: Apache-2.0
2222+references:
2323+- type: software
2424+ doi: 10.5281/zenodo.597993
2525+ title: Xenon
2626+ authors:
2727+ - given-names: Jason
2828+ family-names: Maassen
2929+ affiliation: Netherlands eScience Center
3030+ - given-names: Stefan
3131+ family-names: Verhoeven
3232+ affiliation: Nederlands eScience Center
3333+ - given-names: Joris
3434+ family-names: Borgdorff
3535+ affiliation: '@thehyve'
3636+ - given-names: Niels
3737+ family-names: Drost
3838+ affiliation: Netherlands eScience Center
3939+ - given-names: Jurriaan
4040+ family-names: Spaaks
4141+ name-particle: H.
4242+ affiliation: Netherlands eScience Center
4343+ - given-names: Christiaan
4444+ family-names: Meijer
4545+ affiliation: Netherlands eScience Center
4646+ - given-names: Rob
4747+ family-names: van Nieuwpoort
4848+ name-particle: V.
4949+ affiliation: Netherlands eScience center
5050+ - given-names: Atze
5151+ family-names: van der Ploeg
5252+ affiliation: Netherlands eScience center
5353+ - given-names: Piter
5454+ family-names: de Boer
5555+ name-particle: T.
5656+ affiliation: Netherlands eScience center
5757+ - given-names: Ben
5858+ family-names: van Werkhoven
5959+ affiliation: Netherlands eScience Center
6060+ - given-names: Arnold
6161+ family-names: Kuzniar
6262+ affiliation: Netherlands eScience Center
···11+Borrowed from [`ruby-cff`](https://github.com/citation-file-format/ruby-cff)'s fixtures https://github.com/citation-file-format/ruby-cff/tree/ec3aeff98855690d56ac2cd35f7181e5d9ee26f3/test/files.
···11+# Testing
22+33+## Overview
44+55+We maintain a collection of `.cff` files for testing. In these files we try to cover
66+various possible scenarios described in the specification. During the test, these
77+files are validated using `cffconvert` (a command line utility to read a `.cff` file
88+and convert it to BibTex and other formats). We check that all files are validated or
99+rejected as expected.
1010+1111+## Dependencies
1212+1313+- Python 3.10 or higher
1414+- [cffconvert](https://pypi.org/project/cffconvert/) version 1.0.0 or higher
1515+- [pytest](https://pypi.org/project/pytest/)
1616+1717+You can install `cffconvert` and `pytest` using the `requirements.txt` file from
1818+this directory by entering the following command:
1919+2020+ python3 -m pip install -r requirements.txt
2121+2222+in the root directory of the clone of this repository.
2323+2424+## Files and directories
2525+2626+The tests are organised into subdirectories named after minimal versions of the CFF
2727+specification supporting the tested features.
2828+2929+Each test is placed in a directory with some informative name. We suggest that a name
3030+of the directory with a test for failing validation should start with `fail-`, otherwise
3131+its name may be arbitrary, but we suggest to follow the same style as for existing tests.
3232+3333+Inside the directory for a specific test, there should be two files: a `CITATION.cff`
3434+file to be validated during the test, and a Python script to run the test. The name of
3535+the latter must start with `test_`, followed by some string with underscores, based on
3636+the name of the directory for this specific test (see existing tests for some examples).
3737+To create such a Python script, you can copy and rename an existing test script from some
3838+other test directory, only paying attention whether it checks that validation should
3939+pass or fail - there are only two kinds of test scripts at the moment.
4040+4141+## Running tests
4242+4343+To run tests, enter
4444+4545+ pytest
4646+4747+You should expect to see output similar to this:
4848+4949+```
5050+tests/validate.py::test[./tests/1.1.0/reference-article/CITATION.cff] PASSED [ 2%]
5151+tests/validate.py::test[./tests/1.1.0/reference-art/CITATION.cff] PASSED [ 5%]
5252+tests/validate.py::test[./tests/1.1.0/reference-book/CITATION.cff] PASSED [ 7%]
5353+tests/validate.py::test[./tests/1.1.0/fail-bad-identifier-type-in-root/CITATION.cff] PASSED [ 10%]
5454+5555+...
5656+5757+tests/validate.py::test[./tests/1.0.3/reference-blog/CITATION.cff] PASSED [ 94%]
5858+tests/validate.py::test[./tests/1.0.3/software-container/CITATION.cff] PASSED [ 97%]
5959+tests/validate.py::test[./tests/1.0.3/software-executable/CITATION.cff] PASSED [100%]
6060+6161+```
6262+6363+surrounded with some additional information messages.
···11+import os
22+33+44+def pytest_generate_tests(metafunc):
55+ """Generates a list of relative paths to CITATION.cff files under tests/, and
66+ uses them to parameterize a fixture by the name of 'fixture'."""
77+88+ fixtures = list()
99+ schema_versions = [
1010+ "1.0.3",
1111+ "1.1.0",
1212+ "1.2.0"
1313+ ]
1414+ for schema_version in schema_versions:
1515+ d = os.path.join(".", "examples", schema_version)
1616+ for root, dirs, files in os.walk(d):
1717+ for name in files:
1818+ if name == "CITATION.cff":
1919+ fixtures.append(os.path.join(root, name))
2020+2121+ metafunc.parametrize("fixture", fixtures)
···11+# Citation File Format Tooling Catalogue
22+33+## Index by Environment
44+55+* Libraries:
66+ * [`ruby-cff`](#ruby-cff)
77+88+## Index by Language
99+1010+* Ruby:
1111+ * [`ruby-cff`](#ruby-cff)
1212+1313+## Index by Purpose
1414+1515+* Conversion:
1616+ * [`ruby-cff`](#ruby-cff)
1717+* Creation:
1818+ * [`ruby-cff`](#ruby-cff)
1919+* Maintenance:
2020+ * [`ruby-cff`](#ruby-cff)
2121+* Validation:
2222+ * [`ruby-cff`](#ruby-cff)
2323+2424+## Tools
2525+2626+### Interfaces
2727+2828+These tools provide a complete implementation of CFF in the respective languages
2929+such that one can convert, create, maintain and validate CFF files.
3030+3131+#### `ruby-cff`
3232+3333+The official
3434+[GitHub repository](https://github.com/citation-file-format/ruby-cff)
3535+of the project provides a
3636+[summary](https://github.com/citation-file-format/ruby-cff/blob/main/CITATION.cff)
3737+about the project's purpose.
3838+3939+This library is also used by GitHub to parse `CITATION.cff` files in order to
4040+provide a "Cite this repository" option on a project's main page.