1use crate::schema::validate::{is_iso8601_date, is_iso8601_year, is_orcid, is_raid, is_ror, is_unix_epoch};
7use crate::util::{read_file, Label};
8use crate::License;
9use bon::{builder, Builder};
10use derive_more::Display;
11use schemars::{schema_for, JsonSchema};
12use serde::{Deserialize, Serialize};
13use serde_with::skip_serializing_none;
14use std::path::PathBuf;
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}