acorn_lib/schema/
raid.rs

1//! ## Research activity identifier (RAiD) metadata schema
2//!
3//! See <https://metadata.raid.org/en/v1.6/index.html> for official documentation on schema.
4//!
5//! Use ACORN to generate JSON schema for RAiD metadata with `acorn schema --raid`
6use crate::schema::validate::{is_iso8601_date, is_iso8601_year, is_raid, is_ror};
7use crate::{License, SemanticVersion};
8use bon::{builder, Builder};
9use derive_more::Display;
10use schemars::{schema_for, JsonSchema};
11use serde::{Deserialize, Serialize};
12use serde_with::skip_serializing_none;
13use validator::Validate;
14
15/// Allowed values for access types
16#[derive(Clone, Debug, Default, Deserialize, Display, JsonSchema, Serialize)]
17#[serde(rename = "kebab-case")]
18pub enum AccessType {
19    /// Open access
20    #[default]
21    #[display("open-access")]
22    OpenAccess,
23    /// Embargoed access
24    #[display("embargoed-access")]
25    EmbargoedAccess,
26}
27/// CRediT role
28///
29/// Taxonomy of 14 roles that can be used to describe the key types of contributions typically made to the production and publication of research output such as research articles.
30///
31/// See <https://www.niso.org/publications/z39104-2022-credit>
32#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
33#[serde(rename = "kebab-case")]
34pub enum CreditRole {
35    /// Ideas; formulation or evolution of overarching research goals and aims.
36    #[display("conceptualization")]
37    Conceptualization,
38    /// Management activities to annotate (produce metadata), scrub data and maintain research data (including software code, where it is necessary for interpreting the data itself) for initial use and later re-use.
39    #[display("data-curation")]
40    DataCuration,
41    /// Application of statistical, mathematical, computational, or other formal techniques to analyze or synthesize study data.
42    #[display("formal-analysis")]
43    FormalAnalysis,
44    /// Acquisition of the financial support for the project leading to this publication.
45    #[display("funding-acquisition")]
46    FundingAcquisition,
47    /// Conducting a research and investigation process, specifically performing the experiments, or data/evidence collection.
48    #[display("investigation")]
49    Investigation,
50    /// Development or design of methodology; creation of models.
51    #[display("methodology")]
52    Methodology,
53    /// Management and coordination responsibility for the research activity planning and execution.
54    #[display("project-administration")]
55    ProjectAdministration,
56    /// Provision of study materials, reagents, materials, patients, laboratory samples, animals, instrumentation, computing resources, or other analysis tools.
57    #[display("resources")]
58    Resources,
59    /// Programming, software development; designing computer programs; implementation of the computer code and supporting algorithms; testing of existing code components.
60    #[display("software")]
61    Software,
62    /// Oversight and leadership responsibility for the research activity planning and execution, including mentorship external to the core team.
63    #[display("supervision")]
64    Supervision,
65    /// Verification, whether as a part of the activity or separate, of the overall replication/reproducibility of results/experiments and other research outputs.
66    #[display("validation")]
67    Validation,
68    /// Preparation, creation and/or presentation of the published work, specifically visualization/data presentation.
69    #[display("visualization")]
70    Visualization,
71    /// Preparation, creation and/or presentation of the published work, specifically writing the initial draft (including substantive translation).
72    #[display("writing-original-draft")]
73    WritingOriginalDraft,
74    /// Preparation, creation and/or presentation of the published work by those from the original research group, specifically critical review, commentary or revision - including pre- or post-publication stages
75    #[display("writing-review-editing")]
76    WritingReviewEditing,
77}
78/// Description types
79#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
80#[serde(rename = "kebab-case")]
81pub enum DescriptionType {
82    /// Primary description (i.e., a preferred full description or abstract)
83    Primary,
84    /// An alternative description (i.e., an additional or supplementary full description or abstract)
85    Alternative,
86    /// Brief description (i.e., a shorter version of the primary description)
87    Brief,
88    /// Significance statement
89    #[display("significance-statement")]
90    SignificanceStatement,
91    /// Methods
92    Methods,
93    /// Objectives
94    Objectives,
95    /// Acknowledgements (i.e., for recognition of people not listed as Contributors or organizations not listed as organizations)
96    Acknowledgements,
97    /// Other (i.e., any other descriptive information such as a note)
98    Other,
99}
100/// Flag indicating that a value is affirmative (e.g., for `leader` or `contact`)
101#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)]
102pub enum Flag {
103    /// Affirmative flag
104    Yes,
105}
106/// Category of input, output, or process document
107#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
108#[serde(rename = "kebab-case")]
109pub enum ObjectCategoryType {
110    /// Input
111    #[display("input")]
112    Input,
113    /// Internal process document or artifact
114    #[display("internal-process-document")]
115    InternalProcessDocument,
116    /// Output
117    #[display("output")]
118    Output,
119}
120/// Type of input, output, or process document
121#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
122#[serde(rename = "kebab-case")]
123pub enum ObjectType {
124    /// Audiovisual
125    #[display("audiovisual")]
126    Audiovisual,
127    /// Book
128    #[display("book")]
129    Book,
130    /// Book chapter
131    #[display("book-chapter")]
132    BookChapter,
133    /// Computational notebook (e.g., Jupyter notebook)
134    #[display("computational-notebook")]
135    ComputationalNotebook,
136    /// Conference paper
137    #[display("conference-paper")]
138    ConferencePaper,
139    /// Conference poster
140    #[display("conference-poster")]
141    ConferencePoster,
142    /// Conference proceeding
143    #[display("conference-proceeding")]
144    ConferenceProceeding,
145    /// Data paper
146    #[display("data-paper")]
147    DataPaper,
148    /// Dataset
149    #[display("dataset")]
150    Dataset,
151    /// Dissertation
152    #[display("dissertation")]
153    Dissertation,
154    /// Event
155    #[display("event")]
156    Event,
157    /// Funding
158    ///
159    /// *Note*
160    /// > Includes grants or other cash or in-kind awards, but not prizes
161    #[display("funding")]
162    Funding,
163    /// Image
164    #[display("image")]
165    Image,
166    /// Instrument
167    #[display("instrument")]
168    Instrument,
169    /// Journal article
170    #[display("journal-article")]
171    JournalArticle,
172    /// Learning object
173    #[display("learning-object")]
174    LearningObject,
175    /// Model
176    #[display("model")]
177    Model,
178    /// Output management plan
179    #[display("output-management-plan")]
180    OutputManagementPlan,
181    /// Physical object
182    #[display("physical-object")]
183    PhysicalObject,
184    /// Preprint
185    #[display("preprint")]
186    Preprint,
187    /// Prize (excluding funded awards)
188    #[display("prize")]
189    Prize,
190    /// Report
191    #[display("report")]
192    Report,
193    /// Service
194    #[display("service")]
195    Service,
196    /// Software
197    #[display("software")]
198    Software,
199    /// Sound
200    #[display("sound")]
201    Sound,
202    /// Standard
203    #[display("standard")]
204    Standard,
205    /// Text
206    #[display("text")]
207    Text,
208    /// Workflow
209    #[display("workflow")]
210    Workflow,
211}
212/// Organization role identifier
213#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
214#[serde(rename = "kebab-case")]
215pub enum OrganizationRoleType {
216    /// Lead research organization
217    #[display("lead-research-organization")]
218    LeadResearchOrganization,
219    /// Other research organization
220    #[display("other-research-organization")]
221    OtherResearchOrganization,
222    /// Partner organization (i.e., a non-research organization, such as an industry, government, or community partner that is collaborating on the project or activity, as a research partner rather than a hired consultant or contractor)
223    #[display("partner-organization")]
224    PartnerOrganization,
225    /// Contractor (i.e., a consulting organization hired by the project)
226    #[display("contractor")]
227    Contractor,
228    /// Funder (i.e., an organization underwriting the research via a cash or in-kind grant, prize, or investment, but not otherwise listed as a research organization, partner organization or contractor)
229    #[display("funder")]
230    Funder,
231    /// Facility (i.e., an organization providing access to physical or digital infrastructure, but not otherwise listed as a research organization, partner organization or contractor)
232    #[display("facility")]
233    Facility,
234    /// Other Organiation not covered by the roles above
235    #[display("other-organization")]
236    OtherOrganization,
237}
238/// Represents a contributor's administrative position on a project (such as their position on a grant application)
239///
240/// <div class="warning">Use contributor role to define scientific or scholarly contributions</div>
241#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
242#[serde(rename = "kebab-case")]
243pub enum PositionType {
244    /// Principal Investigator
245    #[display("principal-investigator")]
246    #[serde(alias = "ChiefInvestigator")]
247    PrincipalInvestigator,
248    /// Co-Investigator
249    #[display("co-investigator")]
250    #[serde(alias = "collaborator")]
251    CoInvestigator,
252    /// Partner Investigator (e.g., industry, government, or community collaborator)
253    #[display("partner-investigator")]
254    PartnerInvestigator,
255    /// Consultant (e.g., someone hired as a contract researcher by the project)
256    #[display("consultant")]
257    Consultant,
258    /// Other Participant not covered by one of the positions above, e.g., "member" or "other significant contributor"
259    #[display("other")]
260    Other,
261}
262/// RAiD Relation Type
263///
264/// Describes the relationship being one activity and another
265#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
266#[serde(rename = "kebab-case")]
267pub enum RelatedRaidType {
268    /// Continues
269    Continues,
270    /// Is continued by
271    #[display("is-continued-by")]
272    IsContinuedBy,
273    /// Has part
274    #[display("has-part")]
275    HasPart,
276    /// Is part of
277    #[display("is-part-of")]
278    IsPartOf,
279    /// Is source of
280    #[display("is-source-of")]
281    IsSourceOf,
282    /// Is derived from
283    #[display("is-derived-from")]
284    IsDerivedFrom,
285    /// Obsoletes
286    /// > For resolving duplicate RAiDs
287    Obsoletes,
288    /// Is obsoleted by
289    /// > For resolving duplicate RAiDs
290    #[display("is-obsoleted-by")]
291    IsObsoletedBy,
292}
293/// Allowed values for title identifiers
294#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
295pub enum TitleType {
296    /// Preferred full or long title
297    Primary,
298    /// Abreviated title
299    Short,
300    /// Title acronym
301    Acronym,
302    /// Alternative title, including subtitle or other supplemental title
303    Alternative,
304}
305/// Metadata schema block containing RAiD access information
306///
307/// See <https://metadata.raid.org/en/v1.6/core/access.html>
308#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
309pub struct Access {
310    /// Access type
311    #[serde(rename = "type")]
312    pub access_type: AccessIdentifier,
313    /// Date an embargo on access to the RAiD metadata ends
314    /// ### Format
315    /// > [ISO 8601] standard date (e.g., `YYYY-MM-DD`)
316    ///
317    /// <div class="warning">Mandatory if access type is "embargoed"</div>
318    ///
319    /// <div class="warning">Embargo expiration dates may not lay more than 18 months from the date the RAiD was registered. Year, month, and day mush be specified.</div>
320    ///
321    /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
322    #[validate(custom(function = "is_iso8601_date"))]
323    pub embargo_expiry: Option<String>,
324    /// Access statement
325    ///
326    /// <div class="warning">Mandatory if access type is not "open"</div>
327    #[validate(nested)]
328    pub statement: Option<AccessStatement>,
329}
330/// Access type identifier
331#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
332#[serde(rename = "camelCase")]
333pub struct AccessIdentifier {
334    /// Type of access granted to a RAiD metadata record
335    pub id: AccessType,
336    /// URI of the access type schema
337    #[validate(url)]
338    pub schema_uri: String,
339}
340/// Metadata schema block containing an explanation for any access type that is not "open", with the explanation's associated properties
341#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
342pub struct AccessStatement {
343    /// The text of an access statement that explains any restrictions on access
344    #[validate(length(min = 1, max = 1000))]
345    pub text: Option<String>,
346    /// The language of the access statement
347    #[validate(nested)]
348    pub language: Option<Language>,
349}
350/// Metadata schema block containing alternative local or global identifiers for the project or activity associated with the RAiD
351#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
352pub struct AlternateIdentifier {
353    /// Identifier other than the RAiD applied to the project or activity
354    /// ### Example
355    /// > ACORN research activity data (RAD) [identifier]
356    ///
357    /// [identifier]: ./struct.Metadata.html#structfield.identifier
358    pub id: String,
359    /// Free text description of the type of alternate identifier supplied
360    #[serde(rename = "type")]
361    pub alternate_identifier_type: String,
362}
363/// Link to another website related to the project or activity
364#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
365pub struct AlternateUrl {
366    #[validate(url)]
367    url: String,
368}
369/// Metadata schema block containing a contributor to a RAiD and their associated properties
370///
371/// See <https://metadata.raid.org/en/v1.6/core/contributors.html>
372#[skip_serializing_none]
373#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
374#[serde(rename = "camelCase")]
375#[serde(deny_unknown_fields)]
376pub struct Contributor {
377    /// Contributor (person) associated with a project or activity identified by a persistent identifier (PID)
378    pub id: String,
379    /// URI of the contributor identifier schema
380    ///
381    /// <div class="warning">PID is required and (currently) only [ORCID] and [ISNI] are allowed</div>
382    ///
383    /// [ISNI]: https://isni.org/
384    /// [ORCID]: https://orcid.org/
385    #[validate(url)]
386    pub schema_uri: String,
387    /// Contributor's administrative position on a project or activity
388    pub position: ContributorPosition,
389    /// Flag indicating that the contributor as a project leader
390    ///
391    /// Allowed values: `Yes` or `Null`
392    pub leader: Option<Flag>,
393    /// Flag indicating that the contributor as a project contact
394    ///
395    /// Allowed values: `Yes` or `Null`
396    pub contact: Option<Flag>,
397    /// Contributor's role(s) on a project or activity
398    pub role: Option<Vec<Role>>,
399}
400/// Metadata schema sub-block describing a contributor's administrative position on a project or activity
401///
402/// See <https://metadata.raid.org/en/v1.6/core/contributors.html#contributor-position>
403#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
404#[serde(rename = "camelCase")]
405#[serde(deny_unknown_fields)]
406pub struct ContributorPosition {
407    /// Contributor's administrative position in the project
408    /// ### Example
409    /// > "Principal Investigator"
410    pub id: PositionType,
411    /// URI of the position schema used
412    ///
413    /// <div class="warning">Controlled list of schemas is informed by Simon Cox's [Project Ontology], [OpenAIRE] "Project" guidelines, NIH definitions, ARC definitions, and DataCite Metadata Schema 4.4 Appendix 1 Table 5 "Description of contributorType".</div>
414    ///
415    /// [OpenAIRE]: https://guidelines.openaire.eu/en/latest/
416    /// [Project Ontology]: http://linked.data.gov.au/def/project
417    #[validate(url)]
418    pub schema_uri: String,
419    /// Dates associated with contributor's involvement in a project or activity
420    #[serde(flatten)]
421    pub date: Date,
422}
423///  Start and end dates for the associated metadata
424#[skip_serializing_none]
425#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
426#[serde(rename = "camelCase")]
427#[serde(deny_unknown_fields)]
428pub struct Date {
429    /// Associated data start date
430    /// ### Format
431    /// > [ISO 8601] standard date (e.g., `YYYY-MM-DD`)
432    ///
433    /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
434    #[validate(custom(function = "is_iso8601_date"))]
435    pub start_date: String,
436    /// Associated data end date
437    /// ### Format
438    /// > [ISO 8601] standard date (e.g., `YYYY-MM-DD`)
439    ///
440    /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
441    #[validate(custom(function = "is_iso8601_date"))]
442    pub end_date: Option<String>,
443}
444/// Metadata schema block containing the description of the RAiD and associated properties
445///
446/// See <https://metadata.raid.org/en/v1.6/core/descriptions.html>
447#[skip_serializing_none]
448#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
449#[serde(deny_unknown_fields)]
450pub struct Description {
451    /// Description text
452    #[validate(length(min = 3, max = 1000))]
453    pub text: String,
454    /// Description type information
455    #[serde(rename = "type")]
456    pub description_type: DescriptionIdentifier,
457    /// Language of the description text
458    pub language: Option<Language>,
459}
460/// Metadata schema block declaring the type of description
461#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
462pub struct DescriptionIdentifier {
463    /// Description identifier
464    pub id: DescriptionType,
465    /// URI of the associated description schema
466    #[validate(url)]
467    pub schema_uri: String,
468}
469/// Metadata schema block containing information about the associated type
470#[derive(Builder, Clone, Debug, Deserialize, Serialize, JsonSchema, Validate)]
471#[serde(rename = "camelCase")]
472#[serde(deny_unknown_fields)]
473pub struct Identifier {
474    /// Type identifier
475    pub id: String,
476    /// URI of the associated type schema
477    #[validate(url)]
478    pub schema_uri: String,
479}
480/// Metadata schema block declaring the language of the associated text
481#[derive(Builder, Clone, Debug, Deserialize, Serialize, JsonSchema, Validate)]
482#[serde(rename = "camelCase")]
483#[serde(deny_unknown_fields)]
484pub struct Language {
485    /// Language used for the associated text, identified by a code or another identifier
486    /// ### Examples
487    /// - "eng"
488    /// - "fra"
489    /// - "jpn"
490    ///
491    /// <div class="warning">Limited to <a href="https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes">ISO 639:2023 (Set 3)</a></div>
492    #[validate(length(equal = 3))]
493    pub id: String,
494    /// URI of the associated type schema
495    #[validate(url)]
496    pub schema_uri: String,
497}
498/// Research Activity Identifier (RAiD) Metadata
499#[skip_serializing_none]
500#[derive(Builder, Clone, Debug, Display, Deserialize, Serialize, JsonSchema, Validate)]
501#[builder(start_fn = init)]
502#[display("{} ({identifier})", self.title[0])]
503#[serde(deny_unknown_fields)]
504pub struct Metadata {
505    /// Metadata schema block containing the RAiD name and associated properties
506    #[validate(nested)]
507    pub identifier: MetadataIdentifier,
508    /// Dates associated with the RAiD metadata
509    #[validate(nested)]
510    pub date: Date,
511    /// Title metadata of the RAiD
512    ///
513    /// <div class="warning">One and only one title should be identified as "primary"</div>
514    #[validate(nested, length(min = 1))]
515    pub title: Vec<Title>,
516    /// Description metadata of the RAiD
517    #[validate(nested)]
518    pub description: Option<Vec<Description>>,
519    /// Contributors to the RAiD
520    #[validate(nested, length(min = 1))]
521    pub contributors: Vec<Contributor>,
522    /// Organizations associated with the RAiD
523    ///
524    /// <div class="warning">If only one organization is listed, it's role defaults to "Lead Research Organization"</div>
525    ///
526    /// <div class="warning">One and only one organization should be identified as "Lead Research Organization"</div>
527    #[validate(nested)]
528    pub organization: Option<Vec<Organization>>,
529    /// Related objects associated with the RAiD
530    #[validate(nested)]
531    pub related_object: Option<Vec<RelatedObject>>,
532    /// Alternate identifiers associated with the RAiD
533    #[validate(nested)]
534    pub alternate_identifier: Option<Vec<Identifier>>,
535    /// Alternate URLs associated with the RAiD
536    #[validate(nested)]
537    pub alternate_url: Option<Vec<AlternateUrl>>,
538    /// Related RAiD(s) associated with the RAiD
539    #[validate(nested)]
540    pub related_raid: Option<Vec<RelatedRaid>>,
541    /// Access for the RAiD metadata
542    #[validate(nested)]
543    pub access: Access,
544}
545/// Metadata schema block containing the RAiD name and associated properties
546///
547/// See <https://metadata.raid.org/en/v1.6/core/identifier.html#identifier>
548#[derive(Builder, Clone, Debug, Serialize, Deserialize, Display, JsonSchema, Validate)]
549#[display("{id}")]
550#[serde(rename = "camelCase")]
551#[serde(deny_unknown_fields)]
552pub struct MetadataIdentifier {
553    /// Unique alphanumeric character string that identifies a Research Activity Identifier (RAiD) name
554    /// ### Format
555    /// > `https://raid.org/prefix/suffix`
556    #[validate(custom(function = "is_raid"))]
557    pub id: String,
558    /// URI of the identifier scheme used to identify RAiDs
559    /// ### Example
560    /// > `https://raid.org/`
561    #[validate(url)]
562    pub schema_uri: String,
563    /// Mtadata schema sub-block declaring the Registration Agency that minted the RAiD
564    #[validate(nested)]
565    pub registration_agency: RegistrationAgency,
566    /// The licence, or licence waiver, under which the RAiD metadata record associated with this Identifier has been issued
567    ///
568    /// <div class="warning">Only supports CC-0 (?)</div>
569    pub license: License,
570    /// Version number of the RAiD
571    pub version: SemanticVersion,
572}
573/// Metadata schema block containing the organization associated with a RAiD and its associated properties
574///
575/// See <https://metadata.raid.org/en/v1.6/core/organisations.html#organisation>
576#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
577#[serde(rename = "camelCase")]
578pub struct Organization {
579    /// Organization identifier
580    ///
581    /// <div class="warning">Should be <a href="https://ror.org">ROR</a>, if available</div>
582    pub id: String,
583    /// URI of the organization identifier schema
584    ///
585    /// Only allowed value: `https://ror.org/`
586    #[validate(url, contains(pattern = "https://ror.org"))]
587    pub schema_uri: String,
588    /// Organization role
589    #[validate(nested)]
590    pub role: OrganizationRole,
591}
592/// Organization role
593#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
594#[serde(rename = "camelCase")]
595pub struct OrganizationRole {
596    /// Organization role identifier
597    pub id: OrganizationRoleType,
598    /// URI of the organization role identifier schema
599    #[validate(url)]
600    pub schema_uri: String,
601    /// Date information associated with the organization role
602    #[serde(flatten)]
603    pub date: Date,
604}
605/// Metadata schema sub-block that declares the owner of the RAiD (i.e. the organization requesting the RAiD)
606///
607/// See <https://metadata.raid.org/en/v1.6/core/identifier.html#identifier-owner>
608#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
609#[serde(rename = "camelCase")]
610#[serde(deny_unknown_fields)]
611pub struct Owner {
612    /// Persistent identifier of the legal entity responsible for the RAiD
613    ///
614    /// *Default* ROR of the organization requesting the RAiD
615    /// ### Example
616    /// > `https://ror.org/01qz5mb56` (ORNL)
617    #[validate(custom(function = "is_ror"))]
618    pub id: String,
619    /// URI of the identifier scheme used to identify RAiDs
620    /// ### Example
621    /// > `https://ror.org/`
622    #[validate(url)]
623    pub schema_uri: String,
624    /// Service point (SP) that requested the RAiD
625    /// ### Notes
626    /// - RAiD owners can have multiple SPs
627    /// - SPs do not need to be legal entities
628    /// - List of SPs is maintained by each [`RegistrationAgency`]
629    pub service_point: Vec<String>,
630}
631/// Metadata schema block containing inputs, outputs, and process documents related to a RAiD plus associated properties
632///
633/// See <https://metadata.raid.org/en/v1.6/core/relatedObjects.html>
634#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
635#[serde(rename = "camelCase")]
636#[serde(deny_unknown_fields)]
637pub struct RelatedObject {
638    /// Persistent identifier (PID) of related object
639    ///
640    /// The object can be any combination of
641    /// - input or resource used by a project or activity
642    /// - output or product created by a project or activity
643    /// - internal process documentation used within a project or activity
644    pub id: String,
645    /// URI of the relatedObject identifier schema
646    #[validate(url)]
647    pub schema_uri: String,
648    /// Type information of related object
649    #[serde(rename = "type")]
650    #[validate(nested)]
651    pub related_object_type: RelatedObjectIdentifier,
652    /// Category information of related object
653    #[validate(nested, length(min = 1))]
654    pub category: Vec<RelatedObjectCategory>,
655}
656/// Related object category information
657#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
658#[serde(rename = "camelCase")]
659pub struct RelatedObjectCategory {
660    /// Related object category identifier
661    pub id: ObjectCategoryType,
662    /// URI of the category schema used
663    #[validate(url)]
664    pub schema_uri: String,
665}
666/// Related object identifier
667#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
668#[serde(rename = "camelCase")]
669pub struct RelatedObjectIdentifier {
670    /// Related object type identifier
671    pub id: ObjectType,
672    /// URI of the related object type identifier schema
673    #[validate(url)]
674    pub schema_uri: String,
675}
676/// Metadata schema block containing related RAiDs and qualifying the relationship
677///
678/// See <https://metadata.raid.org/en/v1.6/core/relatedRaids.html>
679#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
680#[serde(rename = "camelCase")]
681pub struct RelatedRaid {
682    /// Subsidiary or otherwise related RAiD
683    pub id: String,
684    /// Related RAiD type
685    #[serde(rename = "type")]
686    pub related_raid_type: RelatedRaidIdentifier,
687}
688/// Related RAiD identifier
689#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
690#[serde(rename = "camelCase")]
691pub struct RelatedRaidIdentifier {
692    /// Related RAiD type identifier
693    pub id: RelatedRaidType,
694    /// URI of the related RAiD type identifier schema
695    #[validate(url)]
696    pub schema_uri: String,
697}
698/// Metadata schema block containing the RAiD name and associated properties
699///
700/// See <https://metadata.raid.org/en/v1.6/core/identifier.html#identifier-registrationagency>
701#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
702#[serde(rename = "camelCase")]
703#[serde(deny_unknown_fields)]
704pub struct RegistrationAgency {
705    /// Persistent identifier of the RAiD Registration Agency that minted the RAiD
706    ///
707    /// *Default* ROR of the RAiD Registration Agency
708    #[validate(custom(function = "is_ror"))]
709    pub id: String,
710    /// URI of the identifier scheme used to identify RAiDs
711    /// ### Example
712    /// > `https://raid.org/`
713    #[validate(url)]
714    pub schema_uri: String,
715}
716/// Metadata schema sub-block describing a contributor's scientific or scholarly role on a project using the [CRediT] vocabulary
717///
718/// See <https://metadata.raid.org/en/v1.6/core/contributors.html#contributor-role>
719///
720/// [CRediT]: https://credit.niso.org/
721#[skip_serializing_none]
722#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
723#[serde(rename = "camelCase")]
724#[serde(deny_unknown_fields)]
725pub struct Role {
726    /// Contributor role on a project or activity
727    pub id: Option<CreditRole>,
728    /// URI of the role schema used
729    #[validate(url)]
730    pub schema_uri: Option<String>,
731}
732/// Metadata schema block containing the title of RAiD and associated properties
733///
734/// See <https://metadata.raid.org/en/v1.6/core/titles.html>
735#[skip_serializing_none]
736#[derive(Builder, Clone, Debug, Display, Serialize, Deserialize, JsonSchema, Validate)]
737#[display("{text} ({title_type})")]
738#[serde(deny_unknown_fields)]
739pub struct Title {
740    /// Name or title by which the project or activity is known
741    #[validate(length(min = 3, max = 100))]
742    pub text: String,
743    /// Metadata schema block containing information about the title type
744    #[serde(rename = "type")]
745    #[validate(nested)]
746    pub title_type: TitleIdentifier,
747    /// Language of the title
748    #[validate(nested)]
749    pub language: Option<Language>,
750    /// Date the project or activity's title began being used
751    /// ### Format
752    /// > [ISO 8601] standard date (e.g., `YYYY-MM-DD`)
753    ///
754    /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
755    #[validate(custom(function = "is_iso8601_date"))]
756    pub start_date: String,
757    /// Date the project or activity title was changed or stopped being used
758    /// ### Format
759    /// > [ISO 8601] standard date (e.g., `YYYY-MM-DD`)
760    ///
761    /// <div class="warning">Only the year is required, month and day are optional</div>
762    ///
763    /// <div class="warning">Listed as "recommended" (optional) and "required"</div>
764    ///
765    /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
766    // TODO: Add support for month and day(?)
767    #[validate(custom(function = "is_iso8601_year"))]
768    pub end_date: String,
769}
770/// Metadata schema block containing information about the title type
771#[derive(Clone, Debug, Serialize, Deserialize, Display, JsonSchema, Validate)]
772#[display("{id}")]
773#[serde(deny_unknown_fields)]
774pub struct TitleIdentifier {
775    /// Title type
776    ///
777    /// <div class="warning">Only one title should be identified as "Primary"</div>
778    pub id: TitleType,
779    /// URI of the title type schema
780    #[validate(url)]
781    pub schema_uri: String,
782}
783impl Metadata {
784    /// Print research activity identifier (RAiD) metadata schema as JSON schema
785    pub fn to_schema() {
786        let schema = schema_for!(Metadata);
787        println!("{}", serde_json::to_string_pretty(&schema).unwrap());
788    }
789}