1use crate::prelude::PathBuf;
7use crate::schema::validate::{is_iso8601_date, is_iso8601_year, is_orcid, is_raid, is_ror, is_unix_epoch};
8use crate::util::{read_file, Label};
9use crate::License;
10use bon::{builder, Builder};
11use derive_more::Display;
12use schemars::{schema_for, JsonSchema};
13use serde::{Deserialize, Serialize};
14use serde_with::skip_serializing_none;
15use tracing::error;
16use validator::Validate;
17
18#[derive(Clone, Debug, Default, Deserialize, Display, JsonSchema, Serialize)]
20#[serde(rename = "kebab-case")]
21pub enum AccessType {
22    #[default]
24    #[display("open-access")]
25    #[serde(alias = "https://vocabularies.coar-repositories.org/access_rights/c_abf2/")]
26    OpenAccess,
27    #[display("embargoed-access")]
29    #[serde(alias = "https://vocabularies.coar-repositories.org/access_rights/c_f1cf/")]
30    EmbargoedAccess,
31}
32#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
38#[serde(rename = "kebab-case")]
39pub enum CreditRole {
40    #[display("conceptualization")]
42    #[serde(alias = "https://credit.niso.org/contributor-roles/conceptualization/")]
43    Conceptualization,
44    #[display("data-curation")]
46    #[serde(alias = "https://credit.niso.org/contributor-roles/data-curation/")]
47    DataCuration,
48    #[display("formal-analysis")]
50    #[serde(alias = "https://credit.niso.org/contributor-roles/formal-analysis/")]
51    FormalAnalysis,
52    #[display("funding-acquisition")]
54    #[serde(alias = "https://credit.niso.org/contributor-roles/funding-acquisition/")]
55    FundingAcquisition,
56    #[display("investigation")]
58    #[serde(alias = "https://credit.niso.org/contributor-roles/investigation/")]
59    Investigation,
60    #[display("methodology")]
62    #[serde(alias = "https://credit.niso.org/contributor-roles/methodology/")]
63    Methodology,
64    #[display("project-administration")]
66    #[serde(alias = "https://credit.niso.org/contributor-roles/project-administration/")]
67    ProjectAdministration,
68    #[display("resources")]
70    #[serde(alias = "https://credit.niso.org/contributor-roles/resources/")]
71    Resources,
72    #[display("software")]
74    #[serde(alias = "https://credit.niso.org/contributor-roles/software/")]
75    Software,
76    #[display("supervision")]
78    #[serde(alias = "https://credit.niso.org/contributor-roles/supervision/")]
79    Supervision,
80    #[display("validation")]
82    #[serde(alias = "https://credit.niso.org/contributor-roles/validation/")]
83    Validation,
84    #[display("visualization")]
86    #[serde(alias = "https://credit.niso.org/contributor-roles/visualization/")]
87    Visualization,
88    #[display("writing-original-draft")]
90    #[serde(alias = "https://credit.niso.org/contributor-roles/writing-original-draft/")]
91    WritingOriginalDraft,
92    #[display("writing-review-editing")]
94    #[serde(alias = "https://credit.niso.org/contributor-roles/writing-review-editing/")]
95    WritingReviewEditing,
96}
97#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
99#[serde(rename = "kebab-case")]
100pub enum DescriptionType {
101    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/318")]
103    Primary,
104    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/319")]
106    Alternative,
107    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/3")]
109    Brief,
110    #[display("significance-statement")]
112    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/9")]
113    SignificanceStatement,
114    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/8")]
116    Methods,
117    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/7")]
119    Objectives,
120    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/392")]
122    Acknowledgements,
123    #[serde(alias = "https://vocabulary.raid.org/description.type.schema/6")]
125    Other,
126}
127#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
129#[serde(rename = "kebab-case")]
130pub enum ObjectCategoryType {
131    #[display("output")]
133    #[serde(alias = "https://vocabulary.raid.org/relatedObject.category.id/190")]
134    Output,
135    #[display("input")]
137    #[serde(alias = "https://vocabulary.raid.org/relatedObject.category.id/191")]
138    Input,
139    #[display("internal-process-document")]
141    #[serde(alias = "https://vocabulary.raid.org/relatedObject.category.id/192")]
142    InternalProcessDocument,
143}
144#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
146#[serde(rename = "kebab-case")]
147pub enum ObjectType {
148    #[display("output-management-plan")]
150    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/247")]
151    OutputManagementPlan,
152    #[display("conference-poster")]
154    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/248")]
155    ConferencePoster,
156    #[display("workflow")]
158    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/249")]
159    Workflow,
160    #[display("journal-article")]
162    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/250")]
163    JournalArticle,
164    #[display("standard")]
166    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/251")]
167    Standard,
168    #[display("report")]
170    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/252")]
171    Report,
172    #[display("dissertation")]
174    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/253")]
175    Dissertation,
176    #[display("preprint")]
178    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/254")]
179    Preprint,
180    #[display("data-paper")]
182    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/255")]
183    DataPaper,
184    #[display("computational-notebook")]
186    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/256")]
187    ComputationalNotebook,
188    #[display("image")]
190    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/257")]
191    Image,
192    #[display("book")]
194    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/258")]
195    Book,
196    #[display("software")]
198    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/259")]
199    Software,
200    #[display("event")]
202    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/260")]
203    Event,
204    #[display("sound")]
206    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/261")]
207    Sound,
208    #[display("conference-proceeding")]
210    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/262")]
211    ConferenceProceeding,
212    #[display("model")]
214    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/263")]
215    Model,
216    #[display("conference-paper")]
218    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/264")]
219    ConferencePaper,
220    #[display("text")]
222    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/265")]
223    Text,
224    #[display("instrument")]
226    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/266")]
227    Instrument,
228    #[display("learning-object")]
230    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/267")]
231    LearningObject,
232    #[display("prize")]
234    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/268")]
235    Prize,
236    #[display("dataset")]
238    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/269")]
239    Dataset,
240    #[display("physical-object")]
242    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/270")]
243    PhysicalObject,
244    #[display("book-chapter")]
246    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/271")]
247    BookChapter,
248    #[display("funding")]
253    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/272")]
254    Funding,
255    #[display("audiovisual")]
257    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/273")]
258    Audiovisual,
259    #[display("service")]
261    #[serde(alias = "https://vocabulary.raid.org/relatedObject.type.schema/274")]
262    Service,
263}
264#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
266#[serde(rename = "kebab-case")]
267pub enum OrganizationRoleType {
268    #[display("lead-research-organization")]
270    #[serde(alias = "https://vocabulary.raid.org/organisation.role.schema/182")]
271    LeadResearchOrganization,
272    #[display("other-research-organization")]
274    #[serde(alias = "https://vocabulary.raid.org/organisation.role.schema/183")]
275    OtherResearchOrganization,
276    #[display("partner-organization")]
278    #[serde(alias = "https://vocabulary.raid.org/organisation.role.schema/184")]
279    PartnerOrganization,
280    #[display("contractor")]
282    #[serde(alias = "https://vocabulary.raid.org/organisation.role.schema/185")]
283    Contractor,
284    #[display("funder")]
286    #[serde(alias = "https://vocabulary.raid.org/organisation.role.schema/186")]
287    Funder,
288    #[display("facility")]
290    #[serde(alias = "https://vocabulary.raid.org/organisation.role.schema/187")]
291    Facility,
292    #[display("other-organization")]
294    #[serde(alias = "https://vocabulary.raid.org/organisation.role.schema/188")]
295    OtherOrganization,
296}
297#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
301#[serde(rename = "kebab-case")]
302pub enum PositionType {
303    #[display("principal-investigator")]
305    #[serde(alias = "ChiefInvestigator", alias = "https://vocabulary.raid.org/contributor.position.schema/307")]
306    PrincipalInvestigator,
307    #[display("co-investigator")]
309    #[serde(alias = "collaborator", alias = "https://vocabulary.raid.org/contributor.position.schema/308")]
310    CoInvestigator,
311    #[display("partner-investigator")]
313    #[serde(alias = "https://vocabulary.raid.org/contributor.position.schema/309")]
314    PartnerInvestigator,
315    #[display("consultant")]
317    #[serde(alias = "https://vocabulary.raid.org/contributor.position.schema/310")]
318    Consultant,
319    #[display("other")]
321    #[serde(alias = "https://vocabulary.raid.org/contributor.position.schema/311")]
322    Other,
323}
324#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
328#[serde(rename = "kebab-case")]
329pub enum RelatedRaidType {
330    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/198")]
333    Obsoletes,
334    #[display("is-source-of")]
336    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/199")]
337    IsSourceOf,
338    #[display("is-derived-from")]
340    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/200")]
341    IsDerivedFrom,
342    #[display("has-part")]
344    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/201")]
345    HasPart,
346    #[display("is-part-of")]
348    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/202")]
349    IsPartOf,
350    #[display("is-continued-by")]
352    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/203")]
353    IsContinuedBy,
354    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/204")]
356    Continues,
357    #[display("is-obsoleted-by")]
360    #[serde(alias = "https://vocabulary.raid.org/relatedRaid.type.schema/205")]
361    IsObsoletedBy,
362}
363#[derive(Clone, Debug, Deserialize, Display, JsonSchema, Serialize)]
365pub enum TitleType {
366    #[serde(alias = "https://vocabulary.raid.org/title.type.schema/156")]
368    Acronym,
369    #[serde(alias = "https://vocabulary.raid.org/title.type.schema/4")]
371    Alternative,
372    #[serde(alias = "https://vocabulary.raid.org/title.type.schema/5")]
374    Primary,
375    #[serde(alias = "https://vocabulary.raid.org/title.type.schema/157")]
377    Short,
378}
379#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
383#[serde(deny_unknown_fields, rename_all = "camelCase")]
384pub struct Access {
385    #[validate(nested)]
387    #[serde(rename = "type")]
388    pub access_type: AccessIdentifier,
389    #[validate(custom(function = "is_iso8601_date"))]
399    pub embargo_expiry: Option<String>,
400    #[validate(nested)]
404    pub statement: Option<AccessStatement>,
405}
406#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
408#[serde(deny_unknown_fields, rename_all = "camelCase")]
409pub struct AccessIdentifier {
410    pub id: AccessType,
412    #[validate(url)]
414    pub schema_uri: Option<String>,
415}
416#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
418#[serde(deny_unknown_fields, rename_all = "camelCase")]
419pub struct AccessStatement {
420    #[validate(length(min = 1, max = 1000))]
422    pub text: Option<String>,
423    #[validate(nested)]
425    pub language: Option<Language>,
426}
427#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
429#[serde(deny_unknown_fields, rename_all = "camelCase")]
430pub struct AlternateIdentifier {
431    pub id: String,
437    #[serde(rename = "type")]
439    pub alternate_identifier_type: String,
440}
441#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
443#[serde(deny_unknown_fields, rename_all = "camelCase")]
444pub struct AlternateUrl {
445    #[validate(url)]
446    url: String,
447}
448#[skip_serializing_none]
452#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
453#[serde(deny_unknown_fields, rename_all = "camelCase")]
454pub struct Contributor {
455    #[validate(custom(function = "is_orcid"))]
461    pub id: String,
462    #[validate(url)]
469    pub schema_uri: Option<String>,
470    pub status: String,
472    pub status_message: Option<String>,
474    #[validate(nested)]
476    pub position: Vec<ContributorPosition>,
477    pub leader: bool,
481    pub contact: bool,
485    #[validate(email)]
487    pub email: Option<String>,
488    #[validate(nested)]
490    pub role: Option<Vec<Role>>,
491    pub uuid: Option<String>,
493}
494#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
498#[serde(deny_unknown_fields, rename_all = "camelCase")]
499pub struct ContributorPosition {
500    pub id: PositionType,
504    #[validate(url)]
511    pub schema_uri: Option<String>,
512    #[validate(nested)]
514    #[serde(flatten)]
515    pub date: Date,
516}
517#[skip_serializing_none]
519#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
520#[serde(deny_unknown_fields, rename_all = "camelCase")]
521pub struct Date {
522    #[validate(custom(function = "is_iso8601_date"))]
528    pub start_date: String,
529    #[validate(custom(function = "is_iso8601_date"))]
535    pub end_date: Option<String>,
536}
537#[skip_serializing_none]
541#[derive(Builder, Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
542#[serde(deny_unknown_fields, rename_all = "camelCase")]
543pub struct Description {
544    #[validate(length(min = 3, max = 1000))]
546    pub text: String,
547    #[validate(nested)]
549    #[serde(rename = "type")]
550    pub description_type: DescriptionIdentifier,
551    #[validate(nested)]
553    pub language: Option<Language>,
554}
555#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, Validate)]
557#[serde(deny_unknown_fields, rename_all = "camelCase")]
558pub struct DescriptionIdentifier {
559    pub id: DescriptionType,
561    #[validate(url)]
563    pub schema_uri: Option<String>,
564}
565#[derive(Builder, Clone, Debug, Deserialize, Serialize, JsonSchema, Validate)]
567#[serde(deny_unknown_fields, rename_all = "camelCase")]
568pub struct Identifier {
569    pub id: String,
571    #[validate(url)]
573    pub schema_uri: Option<String>,
574}
575#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
577#[serde(deny_unknown_fields, rename_all = "camelCase")]
578pub struct Keyword {
579    pub text: String,
581    #[validate(nested)]
583    pub language: Option<Language>,
584}
585#[derive(Builder, Clone, Debug, Deserialize, Serialize, JsonSchema, Validate)]
587#[serde(deny_unknown_fields, rename_all = "camelCase")]
588pub struct Language {
589    #[validate(length(equal = 3))]
597    pub id: String,
598    #[validate(url)]
600    pub schema_uri: Option<String>,
601}
602#[skip_serializing_none]
604#[derive(Builder, Clone, Debug, Display, Deserialize, Serialize, JsonSchema, Validate)]
605#[builder(start_fn = init)]
606#[display("({identifier})")]
607#[serde(deny_unknown_fields, rename_all = "camelCase")]
608pub struct Metadata {
609    #[validate(nested)]
611    pub metadata: Option<MetadataMetadata>,
612    #[validate(nested)]
614    pub identifier: MetadataIdentifier,
615    #[validate(nested)]
617    pub date: Option<Date>,
618    #[validate(nested, length(min = 1))]
622    pub title: Option<Vec<Title>>,
623    #[validate(nested)]
625    pub description: Option<Vec<Description>>,
626    #[validate(nested, length(min = 1))]
628    pub contributor: Vec<Contributor>,
629    #[validate(nested, length(min = 1))]
635    #[serde(alias = "organisation")]
636    pub organization: Vec<Organization>,
637    #[validate(nested)]
639    pub related_object: Option<Vec<RelatedObject>>,
640    #[validate(nested)]
642    pub alternate_identifier: Option<Vec<AlternateIdentifier>>,
643    #[validate(nested)]
645    pub alternate_url: Option<Vec<AlternateUrl>>,
646    #[validate(nested)]
648    pub related_raid: Option<Vec<RelatedRaid>>,
649    #[validate(nested)]
651    pub access: Access,
652    #[validate(nested)]
654    pub traditional_knowledge_label: Option<Vec<TraditionalKnowledgeLabel>>,
655    #[validate(nested)]
657    pub spatial_coverage: Option<Vec<SpatialCoverage>>,
658    #[validate(nested)]
660    pub subject: Option<Vec<Subject>>,
661}
662#[derive(Builder, Clone, Debug, Serialize, Deserialize, Display, JsonSchema, Validate)]
666#[display("{id}")]
667#[serde(deny_unknown_fields, rename_all = "camelCase")]
668pub struct MetadataIdentifier {
669    #[validate(custom(function = "is_raid"))]
673    pub id: String,
674    #[validate(url)]
678    pub schema_uri: Option<String>,
679    #[validate(nested)]
681    pub owner: Owner,
682    #[validate(url)]
684    pub raid_agency_url: String,
685    #[validate(nested)]
687    pub registration_agency: RegistrationAgency,
688    pub license: License,
692    #[validate(range(min = 0))]
694    pub version: u32,
695}
696#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
698#[serde(deny_unknown_fields, rename_all = "camelCase")]
699pub struct MetadataMetadata {
700    #[validate(custom(function = "is_unix_epoch"))]
704    pub created: usize,
705    #[validate(custom(function = "is_unix_epoch"))]
709    pub updated: usize,
710}
711#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
715#[serde(deny_unknown_fields, rename_all = "camelCase")]
716pub struct Organization {
717    #[validate(custom(function = "is_ror"))]
723    pub id: String,
724    #[validate(url, contains(pattern = "https://ror.org"))]
728    pub schema_uri: Option<String>,
729    #[validate(nested)]
731    pub role: Vec<OrganizationRole>,
732}
733#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
735#[serde(deny_unknown_fields, rename_all = "camelCase")]
736pub struct OrganizationRole {
737    pub id: OrganizationRoleType,
739    #[validate(url)]
741    pub schema_uri: Option<String>,
742    #[validate(nested)]
744    #[serde(flatten)]
745    pub date: Date,
746}
747#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
751#[serde(deny_unknown_fields, rename_all = "camelCase")]
752pub struct Owner {
753    #[validate(custom(function = "is_ror"))]
759    pub id: String,
760    #[validate(url)]
764    pub schema_uri: Option<String>,
765    pub service_point: usize,
773}
774#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
776#[serde(deny_unknown_fields, rename_all = "camelCase")]
777pub struct Place {
778    pub text: Option<String>,
782    #[validate(nested)]
784    pub language: Option<Language>,
785}
786#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
790#[serde(deny_unknown_fields, rename_all = "camelCase")]
791pub struct RelatedObject {
792    pub id: String,
799    #[validate(url)]
801    pub schema_uri: Option<String>,
802    #[validate(nested)]
804    #[serde(rename = "type")]
805    pub related_object_type: RelatedObjectIdentifier,
806    #[validate(nested, length(min = 1))]
808    pub category: Vec<RelatedObjectCategory>,
809}
810#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
812#[serde(deny_unknown_fields, rename_all = "camelCase")]
813pub struct RelatedObjectCategory {
814    pub id: ObjectCategoryType,
816    #[validate(url)]
818    pub schema_uri: Option<String>,
819}
820#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
822#[serde(deny_unknown_fields, rename_all = "camelCase")]
823pub struct RelatedObjectIdentifier {
824    pub id: ObjectType,
826    #[validate(url)]
828    pub schema_uri: Option<String>,
829}
830#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
834#[serde(deny_unknown_fields, rename_all = "camelCase")]
835pub struct RelatedRaid {
836    pub id: String,
838    #[validate(nested)]
840    #[serde(rename = "type")]
841    pub related_raid_type: RelatedRaidIdentifier,
842}
843#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
845#[serde(deny_unknown_fields, rename_all = "camelCase")]
846pub struct RelatedRaidIdentifier {
847    pub id: RelatedRaidType,
849    #[validate(url)]
851    pub schema_uri: Option<String>,
852}
853#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
857#[serde(deny_unknown_fields, rename_all = "camelCase")]
858pub struct RegistrationAgency {
859    #[validate(custom(function = "is_ror"))]
863    pub id: String,
864    #[validate(url)]
868    pub schema_uri: Option<String>,
869}
870#[skip_serializing_none]
876#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
877#[serde(deny_unknown_fields, rename_all = "camelCase")]
878pub struct Role {
879    pub id: Option<CreditRole>,
881    #[validate(url)]
883    pub schema_uri: Option<String>,
884}
885#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
889#[serde(deny_unknown_fields, rename_all = "camelCase")]
890pub struct SpatialCoverage {
891    pub id: String,
893    #[validate(url)]
895    pub schema_uri: Option<String>,
896    #[validate(nested)]
898    pub place: Vec<Place>,
899}
900#[derive(Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
904#[serde(deny_unknown_fields, rename_all = "camelCase")]
905pub struct Subject {
906    pub id: String,
908    #[validate(url)]
910    pub schema_uri: Option<String>,
911    pub keyword: Option<Vec<Keyword>>,
913}
914#[skip_serializing_none]
918#[derive(Builder, Clone, Debug, Display, Serialize, Deserialize, JsonSchema, Validate)]
919#[display("{text} ({title_type})")]
920#[serde(deny_unknown_fields, rename_all = "camelCase")]
921pub struct Title {
922    #[validate(length(min = 3, max = 100))]
924    pub text: String,
925    #[validate(nested)]
927    #[serde(rename = "type")]
928    pub title_type: TitleIdentifier,
929    #[validate(nested)]
931    pub language: Option<Language>,
932    #[validate(custom(function = "is_iso8601_date"))]
938    pub start_date: String,
939    #[validate(custom(function = "is_iso8601_year"))]
950    pub end_date: String,
951}
952#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Validate)]
956#[serde(deny_unknown_fields, rename_all = "camelCase")]
957pub struct TraditionalKnowledgeLabel {
958    pub id: String,
962    #[validate(url)]
966    pub schema_uri: Option<String>,
967}
968#[derive(Clone, Debug, Serialize, Deserialize, Display, JsonSchema, Validate)]
970#[display("{id}")]
971#[serde(deny_unknown_fields, rename_all = "camelCase")]
972pub struct TitleIdentifier {
973    pub id: TitleType,
977    #[validate(url)]
979    pub schema_uri: Option<String>,
980}
981impl Metadata {
982    pub fn to_schema() {
984        let schema = schema_for!(Metadata);
985        println!("{}", serde_json::to_string_pretty(&schema).unwrap());
986    }
987    pub fn read(path: PathBuf) -> Option<Metadata> {
989        match read_file(path) {
990            | Ok(data) => match serde_json::from_str(&data) {
991                | Ok(value) => Some(value),
992                | Err(why) => {
993                    println!("=> {} Parse RAiD metadata - {why}", Label::fail());
994                    error!("=> {} Parse RAiD metadata - {why}", Label::fail());
995                    None
996                }
997            },
998            | Err(why) => {
999                println!("=> {} Read RAiD metadata file - {why}", Label::fail());
1000                error!("=> {} Read RAiD metadata file - {why}", Label::fail());
1001                None
1002            }
1003        }
1004    }
1005}