Commit 176190a7 authored by Wohlgemuth, Jason's avatar Wohlgemuth, Jason
Browse files

feat: Initial scaffolding for ods and datacite data models

parent 60c88b55
Loading
Loading
Loading
Loading
Loading
+354 −0
Original line number Diff line number Diff line
//! DataCite metadata schema models
//!
//! These types model the structure found in `datacite.json` fixtures, including
//! DOI records, creators, related identifiers, and other DataCite metadata
use crate::schema::ods;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;

/// Collection of DataCite DOI records
pub type Catalog = Vec<Record>;
/// Description type enumeration
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub enum DescriptionType {
    /// Abstract
    #[serde(rename = "Abstract")]
    Abstract,
    /// Methods
    #[serde(rename = "Methods")]
    Methods,
    /// Series information
    #[serde(rename = "SeriesInformation")]
    SeriesInformation,
    /// Table of contents
    #[serde(rename = "TableOfContents")]
    TableOfContents,
    /// Technical info
    #[serde(rename = "TechnicalInfo")]
    TechnicalInfo,
    /// Other
    #[serde(rename = "Other")]
    Other,
}
/// Name type enumeration
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub enum NameType {
    /// Personal name
    #[serde(rename = "Personal")]
    Personal,
    /// Organizational name
    #[serde(rename = "Organizational")]
    Organizational,
}
/// Related identifier type enumeration
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub enum RelatedIdentifierType {
    /// DOI
    #[serde(rename = "DOI")]
    Doi,
    /// URL
    #[serde(rename = "URL")]
    Url,
    /// URN
    #[serde(rename = "URN")]
    Urn,
    /// ISBN
    #[serde(rename = "ISBN")]
    Isbn,
    /// ISSN
    #[serde(rename = "ISSN")]
    Issn,
    /// PMID
    #[serde(rename = "PMID")]
    Pmid,
    /// WOSID
    #[serde(rename = "WOSID")]
    Wosid,
    /// ARK
    #[serde(rename = "ARK")]
    Ark,
    /// ROR
    #[serde(rename = "ROR")]
    Ror,
    /// Other
    #[serde(rename = "Other")]
    Other,
}
/// Relation type enumeration
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub enum RelationType {
    /// Is cited by
    #[serde(rename = "IsCitedBy")]
    IsCitedBy,
    /// Cites
    #[serde(rename = "Cites")]
    Cites,
    /// Is supplement to
    #[serde(rename = "IsSupplementTo")]
    IsSupplementTo,
    /// Is supplemented by
    #[serde(rename = "IsSupplementedBy")]
    IsSupplementedBy,
    /// Is continued by
    #[serde(rename = "IsContinuedBy")]
    IsContinuedBy,
    /// Continues
    #[serde(rename = "Continues")]
    Continues,
    /// Is new version of
    #[serde(rename = "IsNewVersionOf")]
    IsNewVersionOf,
    /// Is previous version of
    #[serde(rename = "IsPreviousVersionOf")]
    IsPreviousVersionOf,
    /// Is version of
    #[serde(rename = "IsVersionOf")]
    IsVersionOf,
    /// Has version
    #[serde(rename = "HasVersion")]
    HasVersion,
    /// Is part of
    #[serde(rename = "IsPartOf")]
    IsPartOf,
    /// Has part
    #[serde(rename = "HasPart")]
    HasPart,
    /// Is referenced by
    #[serde(rename = "IsReferencedBy")]
    IsReferencedBy,
    /// References
    #[serde(rename = "References")]
    References,
    /// Other
    #[serde(rename = "Other")]
    Other,
}
/// General resource type enumeration
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub enum ResourceTypeGeneral {
    /// Dataset
    #[serde(rename = "Dataset")]
    Dataset,
    /// Software
    #[serde(rename = "Software")]
    Software,
    /// Collection
    #[serde(rename = "Collection")]
    Collection,
    /// Journal article
    #[serde(rename = "JournalArticle")]
    JournalArticle,
    /// Book
    #[serde(rename = "Book")]
    Book,
    /// Other
    #[serde(rename = "Other")]
    Other,
}
/// Creator affiliation
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Affiliation {
    /// Affiliation name
    pub name: String,
    /// Affiliation identifier
    #[serde(rename = "affiliationIdentifier")]
    pub affiliation_identifier: Option<String>,
    /// Affiliation identifier scheme
    #[serde(rename = "affiliationIdentifierScheme")]
    pub affiliation_identifier_scheme: Option<String>,
}
/// Record attributes
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Attributes {
    /// DOI string
    pub doi: String,
    /// Publication event type
    pub event: Option<String>,
    /// Dataset titles
    pub titles: Option<Vec<Title>>,
    /// Dataset creators
    pub creators: Option<Vec<Creator>>,
    /// Publisher information
    pub publisher: Option<Publisher>,
    /// Publication year
    #[serde(rename = "publicationYear")]
    pub publication_year: Option<i32>,
    /// Resource types
    #[serde(rename = "types")]
    pub resource_types: Option<ResourceTypes>,
    /// URL to the resource
    pub url: Option<String>,
    /// Descriptions of the resource
    pub descriptions: Option<Vec<Description>>,
    /// Subject keywords
    pub subjects: Option<Vec<Subject>>,
    /// Language code
    pub language: Option<String>,
    /// Related identifiers
    #[serde(rename = "relatedIdentifiers")]
    pub related_identifiers: Option<Vec<RelatedIdentifier>>,
    /// DataCite schema version
    #[serde(rename = "schemaVersion")]
    pub schema_version: Option<String>,
}
/// Creator or author information
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Creator {
    /// Creator name
    pub name: String,
    /// Name type
    #[serde(rename = "nameType")]
    pub name_type: Option<NameType>,
    /// Given name
    #[serde(rename = "givenName")]
    pub given_name: Option<String>,
    /// Family name
    #[serde(rename = "familyName")]
    pub family_name: Option<String>,
    /// Name identifiers
    #[serde(rename = "nameIdentifiers")]
    pub name_identifiers: Option<Vec<NameIdentifier>>,
    /// Creator affiliations
    pub affiliation: Option<Vec<Affiliation>>,
}
/// Resource description
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Description {
    /// Description text
    pub description: String,
    /// Description type
    #[serde(rename = "descriptionType")]
    pub description_type: Option<DescriptionType>,
    /// Description language
    pub language: Option<String>,
}
/// Persistent identifier for a creator
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct NameIdentifier {
    /// Identifier value
    #[serde(rename = "nameIdentifier")]
    pub name_identifier: String,
    /// Identifier scheme
    #[serde(rename = "nameIdentifierScheme")]
    pub name_identifier_scheme: Option<String>,
    /// Scheme URI
    #[serde(rename = "schemeUri")]
    pub scheme_uri: Option<String>,
}
/// Publisher information
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Publisher {
    /// Publisher name
    pub name: String,
    /// Publisher location
    #[serde(rename = "publisherLocation")]
    pub publisher_location: Option<String>,
}
/// Top-level DataCite DOI record
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Record {
    /// Unique DOI identifier
    pub id: String,
    /// Record type (typically "dois")
    #[serde(rename = "type")]
    pub r#type: String,
    /// Record attributes containing metadata
    pub attributes: Attributes,
}
/// Related identifier
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct RelatedIdentifier {
    /// Related identifier value
    #[serde(rename = "relatedIdentifier")]
    pub related_identifier: String,
    /// Related identifier type
    #[serde(rename = "relatedIdentifierType")]
    pub related_identifier_type: Option<RelatedIdentifierType>,
    /// Relation type
    #[serde(rename = "relationType")]
    pub relation_type: Option<RelationType>,
    /// Resource type of the related identifier
    #[serde(rename = "resourceTypeGeneral")]
    pub resource_type_general: Option<ResourceTypeGeneral>,
}
/// Resource type information
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct ResourceTypes {
    /// General resource type
    #[serde(rename = "resourceTypeGeneral")]
    pub resource_type_general: Option<ResourceTypeGeneral>,
    /// Specific resource type
    #[serde(rename = "resourceType")]
    pub resource_type: Option<String>,
}
/// Subject keyword
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Subject {
    /// Subject keyword
    pub subject: String,
    /// Subject language
    pub language: Option<String>,
    /// Subject scheme
    #[serde(rename = "subjectScheme")]
    pub subject_scheme: Option<String>,
}
/// Dataset title
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Title {
    /// Title text
    pub title: String,
    /// Title language code
    #[serde(rename = "titleType")]
    pub title_type: Option<String>,
}
impl From<ods::Dataset> for Record {
    fn from(dataset: ods::Dataset) -> Self {
        let mut titles = None;
        let mut language = None;
        if let Some(ref meta) = dataset.metas.r#default {
            if let Some(ref title) = meta.title {
                titles = Some(vec![Title {
                    title: title.clone(),
                    title_type: None,
                }]);
            }
            language = meta.language.clone();
        }
        let attributes = Attributes {
            doi: dataset.dataset_id.clone(),
            event: None,
            titles,
            creators: None,
            publisher: None,
            publication_year: None,
            resource_types: None,
            url: None,
            descriptions: None,
            subjects: None,
            language,
            related_identifiers: None,
            schema_version: None,
        };
        Record {
            id: dataset.dataset_id,
            r#type: "dois".to_string(),
            attributes,
        }
    }
}
/// Convert an ODS catalog into a DataCite catalog
pub fn from_ods(catalog: ods::Catalog) -> Catalog {
    catalog.into_iter().map(Record::from).collect()
}
+2 −0
Original line number Diff line number Diff line
@@ -41,7 +41,9 @@ use tracing::{debug, error, trace};
use validator::Validate;

pub mod constants;
pub mod datacite;
pub mod graph;
pub mod ods;
pub mod pid;
pub mod prompt;
pub mod research_activity;
+411 −0
Original line number Diff line number Diff line
//! OpenDataSoft (ODS) dataset schema models
//!
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::skip_serializing_none;

/// Collection of ODS datasets
pub type Catalog = Vec<Dataset>;
/// Dublin Core type enumeration
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub enum DublinCoreType {
    /// Dataset entry
    #[serde(rename = "Dataset")]
    Dataset,
}
/// Custom template metadata block
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct CustomTemplate {
    /// Projects field (custom template)
    pub projects: Option<String>,
    /// Source-of-data field
    #[serde(rename = "source-of-data")]
    pub source_of_data: Option<String>,
}
/// Top-level dataset container from ODS exports
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Dataset {
    /// Unique dataset identifier
    pub dataset_id: String,
    /// Whether the dataset has attachments
    pub has_attachments: bool,
    /// Number of attachments
    pub attachments_count: u64,
    /// Whether the dataset has records
    pub has_records: bool,
    /// Field definitions or schema details
    pub fields: Value,
    /// Structured metadata blocks
    pub metas: Meta,
    /// Feature records associated with the dataset
    pub features: Vec<Value>,
}
/// Metadata sections attached to a dataset
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Meta {
    /// DCAT metadata
    pub dcat: Option<Dcat>,
    /// Default metadata
    #[serde(rename = "default")]
    pub r#default: Option<DefaultMeta>,
    /// Dublin Core metadata
    #[serde(rename = "dublin-core")]
    pub dublin_core: Option<DublinCore>,
    /// DCAT-AP metadata
    pub dcat_ap: Option<DcatAp>,
    /// Custom template metadata
    #[serde(rename = "custom-template")]
    pub custom_template: Option<CustomTemplate>,
    /// DataCite metadata
    pub datacite: Option<Datacite>,
}
/// DCAT metadata block
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct Dcat {
    /// Creation timestamp
    pub created: Option<String>,
    /// Issued timestamp
    pub issued: Option<String>,
    /// Creator name
    pub creator: Option<String>,
    /// Contributor name
    pub contributor: Option<String>,
    /// Contact name
    pub contact_name: Option<String>,
    /// Contact email
    pub contact_email: Option<String>,
    /// Accrual periodicity
    #[serde(rename = "accrualperiodicity")]
    pub accrual_periodicity: Option<String>,
    /// Spatial coverage
    pub spatial: Option<String>,
    /// Temporal coverage
    pub temporal: Option<String>,
    /// Granularity description
    pub granularity: Option<String>,
    /// Data quality notes
    #[serde(rename = "dataquality")]
    pub data_quality: Option<String>,
    /// Publisher type
    pub publisher_type: Option<String>,
    /// Conformance reference
    pub conforms_to: Option<String>,
    /// Temporal coverage start
    pub temporal_coverage_start: Option<String>,
    /// Temporal coverage end
    pub temporal_coverage_end: Option<String>,
    /// Access rights
    #[serde(rename = "accessRights")]
    pub access_rights: Option<String>,
    /// Relation reference
    pub relation: Option<String>,
}
/// Default metadata block
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct DefaultMeta {
    /// Dataset title
    pub title: Option<String>,
    /// Dataset description (often HTML)
    pub description: Option<String>,
    /// Theme labels
    pub theme: Option<Vec<String>>,
    /// Keyword labels
    pub keyword: Option<Vec<String>>,
    /// License identifier
    pub license: Option<String>,
    /// License URL
    pub license_url: Option<String>,
    /// Language code
    pub language: Option<String>,
    /// Metadata language codes
    pub metadata_languages: Option<Vec<String>>,
    /// Timezone string
    pub timezone: Option<String>,
    /// Last modified timestamp
    pub modified: Option<String>,
    /// Update metadata on metadata changes
    pub modified_updates_on_metadata_change: Option<bool>,
    /// Update metadata on data changes
    pub modified_updates_on_data_change: Option<bool>,
    /// Data processed timestamp
    pub data_processed: Option<String>,
    /// Metadata processed timestamp
    pub metadata_processed: Option<String>,
    /// Geographic reference
    pub geographic_reference: Option<String>,
    /// Whether the geographic reference is automatic
    pub geographic_reference_auto: Option<bool>,
    /// Territorial reference
    pub territory: Option<String>,
    /// Geometry type labels
    pub geometry_types: Option<Vec<String>>,
    /// Bounding box definition
    pub bbox: Option<String>,
    /// Publisher name
    pub publisher: Option<String>,
    /// Reference links
    pub references: Option<String>,
    /// Record count
    pub records_count: Option<u64>,
    /// Attribution text
    pub attributions: Option<String>,
    /// Source domain identifier
    pub source_domain: Option<String>,
    /// Source domain title
    pub source_domain_title: Option<String>,
    /// Source domain address
    pub source_domain_address: Option<String>,
    /// Source dataset identifier
    pub source_dataset: Option<String>,
    /// Shared catalog identifier
    pub shared_catalog: Option<String>,
    /// Whether the dataset is federated
    pub federated: Option<bool>,
    /// Parent domain identifier
    pub parent_domain: Option<String>,
    /// Update frequency
    pub update_frequency: Option<String>,
}
/// Dublin Core metadata block
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct DublinCore {
    /// Title of the dataset
    pub title: Option<String>,
    /// Alternative title
    pub alternative: Option<String>,
    /// Subject keywords
    pub subject: Option<Vec<String>>,
    /// Description text (often HTML)
    pub description: Option<String>,
    /// Abstract summary
    #[serde(rename = "abstract")]
    pub abstract_text: Option<String>,
    /// Table of content or outline
    #[serde(rename = "tableOfContent")]
    pub table_of_content: Option<String>,
    /// Dublin Core type
    #[serde(rename = "type")]
    pub r#type: Option<DublinCoreType>,
    /// Language code
    pub language: Option<String>,
    /// Coverage notes
    pub coverage: Option<String>,
    /// Spatial coverage
    pub spatial: Option<String>,
    /// Temporal start
    #[serde(rename = "temporal_start")]
    pub temporal_start: Option<String>,
    /// Temporal end
    #[serde(rename = "temporal_end")]
    pub temporal_end: Option<String>,
    /// Relation reference
    pub relation: Option<String>,
    /// Source reference
    pub source: Option<String>,
    /// Replaces reference
    pub replaces: Option<String>,
    /// References value
    pub references: Option<String>,
    /// Requirements value
    pub requires: Option<String>,
    /// Conforms-to reference
    #[serde(rename = "conformsTo")]
    pub conforms_to: Option<String>,
    /// Has-format reference
    #[serde(rename = "hasFormat")]
    pub has_format: Option<String>,
    /// Has-part reference
    #[serde(rename = "hasPart")]
    pub has_part: Option<String>,
    /// Has-version reference
    #[serde(rename = "hasVersion")]
    pub has_version: Option<String>,
    /// Is-format-of reference
    #[serde(rename = "isFormatOf")]
    pub is_format_of: Option<String>,
    /// Is-part-of reference
    #[serde(rename = "isPartOf")]
    pub is_part_of: Option<String>,
    /// Is-version-of reference
    #[serde(rename = "isVersionOf")]
    pub is_version_of: Option<String>,
    /// Is-referenced-by reference
    #[serde(rename = "isReferencedBy")]
    pub is_referenced_by: Option<String>,
    /// Is-replaced-by reference
    #[serde(rename = "isReplacedBy")]
    pub is_replaced_by: Option<String>,
    /// Is-required-by reference
    #[serde(rename = "isRequiredBy")]
    pub is_required_by: Option<String>,
    /// Contributor names
    pub contributor: Option<Vec<String>>,
    /// Creator name
    pub creator: Option<String>,
    /// Publisher name
    pub publisher: Option<String>,
    /// Rights statement
    pub rights: Option<String>,
    /// Access rights
    #[serde(rename = "accessRights")]
    pub access_rights: Option<String>,
    /// License identifier
    pub license: Option<String>,
    /// Date start
    #[serde(rename = "date_start")]
    pub date_start: Option<String>,
    /// Date end
    #[serde(rename = "date_end")]
    pub date_end: Option<String>,
    /// Availability start date
    #[serde(rename = "available_start")]
    pub available_start: Option<String>,
    /// Availability end date
    #[serde(rename = "available_end")]
    pub available_end: Option<String>,
    /// Created date
    pub created: Option<String>,
    /// Accepted date
    #[serde(rename = "dateAccepted")]
    pub date_accepted: Option<String>,
    /// Copyrighted date
    #[serde(rename = "dateCopyrighted")]
    pub date_copyrighted: Option<String>,
    /// Submitted date
    #[serde(rename = "dateSubmitted")]
    pub date_submitted: Option<String>,
    /// Issued date
    pub issued: Option<String>,
    /// Modified date
    pub modified: Option<String>,
    /// Valid start date
    #[serde(rename = "valid_start")]
    pub valid_start: Option<String>,
    /// Valid end date
    #[serde(rename = "valid_end")]
    pub valid_end: Option<String>,
    /// Format identifier
    pub format: Option<String>,
    /// Extent string
    pub extent: Option<String>,
    /// Medium string
    pub medium: Option<String>,
    /// Identifier value
    pub identifier: Option<String>,
    /// Bibliographic citation
    #[serde(rename = "bibliographicCitation")]
    pub bibliographic_citation: Option<String>,
    /// Rights holder
    #[serde(rename = "rightsHolder")]
    pub rights_holder: Option<String>,
    /// Accrual method
    #[serde(rename = "accrualMethod")]
    pub accrual_method: Option<String>,
    /// Accrual periodicity
    #[serde(rename = "accrualPeriodicity")]
    pub accrual_periodicity: Option<String>,
    /// Accrual policy
    #[serde(rename = "accrualPolicy")]
    pub accrual_policy: Option<String>,
    /// Audience
    pub audience: Option<String>,
    /// Education level
    #[serde(rename = "educationLevel")]
    pub education_level: Option<String>,
    /// Instructional method
    #[serde(rename = "instructionalMethod")]
    pub instructional_method: Option<String>,
    /// Mediator
    pub mediator: Option<String>,
    /// Provenance description
    pub provenance: Option<String>,
}
/// DCAT-AP metadata block
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct DcatAp {
    /// Title
    pub title: Option<String>,
    /// Description
    pub description: Option<String>,
    /// Theme label
    pub theme: Option<String>,
    /// Keyword labels
    pub keyword: Option<Vec<String>>,
    /// Contact name
    pub contact_name: Option<String>,
    /// Contact email
    pub contact_email: Option<String>,
    /// Publisher name
    pub publisher_name: Option<String>,
    /// Publisher type
    pub publisher_type: Option<String>,
    /// Spatial bounding box
    pub spatial_bbox: Option<String>,
    /// Spatial centroid
    pub spatial_centroid: Option<String>,
    /// Temporal start date
    #[serde(rename = "temporal_startDate")]
    pub temporal_start_date: Option<String>,
    /// Temporal end date
    #[serde(rename = "temporal_endDate")]
    pub temporal_end_date: Option<String>,
    /// Accrual periodicity
    #[serde(rename = "accrualPeriodicity")]
    pub accrual_periodicity: Option<String>,
}
/// DataCite metadata block
#[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub struct Datacite {
    /// Identifier value
    pub identifier: Option<String>,
    /// Title value
    pub title: Option<String>,
    /// Alternative title
    pub alternative_title: Option<String>,
    /// Publisher name
    pub publisher: Option<String>,
    /// Creator value
    pub creator: Option<Value>,
    /// Publication year
    pub publication_year: Option<String>,
    /// Subject keywords
    pub subject: Option<Vec<String>>,
    /// Contributor value
    pub contributor: Option<Value>,
    /// Date value
    pub date: Option<Value>,
    /// Language value
    pub language: Option<String>,
    /// Resource type
    pub resource_type: Option<Value>,
    /// Alternate identifier
    pub alternate_identifier: Option<String>,
    /// Related identifier
    #[serde(rename = "relatedidentifier")]
    pub related_identifier: Option<Value>,
    /// Size string
    pub size: Option<String>,
    /// Format string
    pub format: Option<String>,
    /// Version string
    pub version: Option<String>,
    /// Rights statement
    pub rights: Option<Value>,
    /// Description text
    pub description: Option<String>,
    /// Geolocation value
    pub geolocation: Option<String>,
}
+1 −0
Original line number Diff line number Diff line
+3 −0
Original line number Diff line number Diff line
@@ -737,3 +737,6 @@ fn test_validate_is_unix_epoch() {
        assert!(is_unix_epoch(*x).is_err(), "=> [REASON] \"{x}\" IS a valid Unix epoch timestamp");
    }
}

mod datacite;
mod ods;
Loading