use crate::error::{MetadataError, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataCiteMetadata {
pub identifier: Identifier,
pub creators: Vec<Creator>,
pub titles: Vec<Title>,
pub publisher: String,
pub publication_year: u16,
pub resource_type: ResourceType,
pub subjects: Vec<Subject>,
pub contributors: Vec<Contributor>,
pub dates: Vec<Date>,
pub language: Option<String>,
pub alternate_identifiers: Vec<AlternateIdentifier>,
pub related_identifiers: Vec<RelatedIdentifier>,
pub sizes: Vec<String>,
pub formats: Vec<String>,
pub version: Option<String>,
pub rights_list: Vec<Rights>,
pub descriptions: Vec<Description>,
pub geo_locations: Vec<GeoLocation>,
pub funding_references: Vec<FundingReference>,
}
pub struct DataCiteBuilder {
identifier: Option<Identifier>,
creators: Vec<Creator>,
titles: Vec<Title>,
publisher: Option<String>,
publication_year: Option<u16>,
resource_type: Option<ResourceType>,
subjects: Vec<Subject>,
contributors: Vec<Contributor>,
dates: Vec<Date>,
language: Option<String>,
alternate_identifiers: Vec<AlternateIdentifier>,
related_identifiers: Vec<RelatedIdentifier>,
sizes: Vec<String>,
formats: Vec<String>,
version: Option<String>,
rights_list: Vec<Rights>,
descriptions: Vec<Description>,
geo_locations: Vec<GeoLocation>,
funding_references: Vec<FundingReference>,
}
impl DataCiteMetadata {
pub fn builder() -> DataCiteBuilder {
DataCiteBuilder {
identifier: None,
creators: Vec::new(),
titles: Vec::new(),
publisher: None,
publication_year: None,
resource_type: None,
subjects: Vec::new(),
contributors: Vec::new(),
dates: Vec::new(),
language: None,
alternate_identifiers: Vec::new(),
related_identifiers: Vec::new(),
sizes: Vec::new(),
formats: Vec::new(),
version: None,
rights_list: Vec::new(),
descriptions: Vec::new(),
geo_locations: Vec::new(),
funding_references: Vec::new(),
}
}
}
impl DataCiteBuilder {
pub fn identifier(mut self, identifier: impl Into<String>, id_type: IdentifierType) -> Self {
self.identifier = Some(Identifier {
identifier: identifier.into(),
identifier_type: id_type,
});
self
}
pub fn creator(mut self, creator: Creator) -> Self {
self.creators.push(creator);
self
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.titles.push(Title {
title: title.into(),
title_type: None,
lang: None,
});
self
}
pub fn publisher(mut self, publisher: impl Into<String>) -> Self {
self.publisher = Some(publisher.into());
self
}
pub fn publication_year(mut self, year: u16) -> Self {
self.publication_year = Some(year);
self
}
pub fn resource_type(mut self, resource_type: ResourceTypeGeneral) -> Self {
self.resource_type = Some(ResourceType {
resource_type_general: resource_type,
resource_type: None,
});
self
}
pub fn subject(mut self, subject: Subject) -> Self {
self.subjects.push(subject);
self
}
pub fn description(mut self, description: Description) -> Self {
self.descriptions.push(description);
self
}
pub fn rights(mut self, rights: Rights) -> Self {
self.rights_list.push(rights);
self
}
pub fn build(self) -> Result<DataCiteMetadata> {
let identifier = self
.identifier
.ok_or_else(|| MetadataError::MissingField("identifier".to_string()))?;
if self.creators.is_empty() {
return Err(MetadataError::MissingField("creators".to_string()));
}
if self.titles.is_empty() {
return Err(MetadataError::MissingField("titles".to_string()));
}
let publisher = self
.publisher
.ok_or_else(|| MetadataError::MissingField("publisher".to_string()))?;
let publication_year = self
.publication_year
.ok_or_else(|| MetadataError::MissingField("publication_year".to_string()))?;
let resource_type = self
.resource_type
.ok_or_else(|| MetadataError::MissingField("resource_type".to_string()))?;
Ok(DataCiteMetadata {
identifier,
creators: self.creators,
titles: self.titles,
publisher,
publication_year,
resource_type,
subjects: self.subjects,
contributors: self.contributors,
dates: self.dates,
language: self.language,
alternate_identifiers: self.alternate_identifiers,
related_identifiers: self.related_identifiers,
sizes: self.sizes,
formats: self.formats,
version: self.version,
rights_list: self.rights_list,
descriptions: self.descriptions,
geo_locations: self.geo_locations,
funding_references: self.funding_references,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Identifier {
pub identifier: String,
pub identifier_type: IdentifierType,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum IdentifierType {
ARK,
ArXiv,
Bibcode,
#[serde(rename = "DOI")]
Doi,
EAN13,
EISSN,
Handle,
IGSN,
ISBN,
ISSN,
ISTC,
LISSN,
LSID,
PMID,
PURL,
UPC,
URL,
URN,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Creator {
pub name: String,
pub name_type: Option<NameType>,
pub given_name: Option<String>,
pub family_name: Option<String>,
pub name_identifiers: Vec<NameIdentifier>,
pub affiliations: Vec<String>,
}
impl Creator {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
name_type: Some(NameType::Personal),
given_name: None,
family_name: None,
name_identifiers: Vec::new(),
affiliations: Vec::new(),
}
}
pub fn organization(name: impl Into<String>) -> Self {
Self {
name: name.into(),
name_type: Some(NameType::Organizational),
given_name: None,
family_name: None,
name_identifiers: Vec::new(),
affiliations: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum NameType {
Organizational,
Personal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NameIdentifier {
pub name_identifier: String,
pub name_identifier_scheme: String,
pub scheme_uri: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Title {
pub title: String,
pub title_type: Option<TitleType>,
pub lang: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum TitleType {
AlternativeTitle,
Subtitle,
TranslatedTitle,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceType {
pub resource_type_general: ResourceTypeGeneral,
pub resource_type: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ResourceTypeGeneral {
Audiovisual,
Book,
BookChapter,
Collection,
ComputationalNotebook,
ConferencePaper,
ConferenceProceeding,
DataPaper,
Dataset,
Dissertation,
Event,
Image,
InteractiveResource,
Journal,
JournalArticle,
Model,
OutputManagementPlan,
PeerReview,
PhysicalObject,
Preprint,
Report,
Service,
Software,
Sound,
Standard,
Text,
Workflow,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Subject {
pub subject: String,
pub subject_scheme: Option<String>,
pub scheme_uri: Option<String>,
pub value_uri: Option<String>,
pub classification_code: Option<String>,
}
impl Subject {
pub fn new(subject: impl Into<String>) -> Self {
Self {
subject: subject.into(),
subject_scheme: None,
scheme_uri: None,
value_uri: None,
classification_code: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contributor {
pub name: String,
pub contributor_type: ContributorType,
pub name_type: Option<NameType>,
pub given_name: Option<String>,
pub family_name: Option<String>,
pub name_identifiers: Vec<NameIdentifier>,
pub affiliations: Vec<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ContributorType {
ContactPerson,
DataCollector,
DataCurator,
DataManager,
Distributor,
Editor,
HostingInstitution,
Producer,
ProjectLeader,
ProjectManager,
ProjectMember,
RegistrationAgency,
RegistrationAuthority,
RelatedPerson,
Researcher,
ResearchGroup,
RightsHolder,
Sponsor,
Supervisor,
WorkPackageLeader,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Date {
pub date: String,
pub date_type: DateType,
pub date_information: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum DateType {
Accepted,
Available,
Collected,
Copyrighted,
Created,
Issued,
Submitted,
Updated,
Valid,
Withdrawn,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlternateIdentifier {
pub alternate_identifier: String,
pub alternate_identifier_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelatedIdentifier {
pub related_identifier: String,
pub related_identifier_type: IdentifierType,
pub relation_type: RelationType,
pub related_metadata_scheme: Option<String>,
pub scheme_uri: Option<String>,
pub scheme_type: Option<String>,
pub resource_type_general: Option<ResourceTypeGeneral>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum RelationType {
IsCitedBy,
Cites,
IsSupplementTo,
IsSupplementedBy,
IsContinuedBy,
Continues,
Describes,
IsDescribedBy,
HasMetadata,
IsMetadataFor,
HasVersion,
IsVersionOf,
IsNewVersionOf,
IsPreviousVersionOf,
IsPartOf,
HasPart,
IsReferencedBy,
References,
IsDocumentedBy,
Documents,
IsCompiledBy,
Compiles,
IsVariantFormOf,
IsOriginalFormOf,
IsIdenticalTo,
IsReviewedBy,
Reviews,
IsDerivedFrom,
IsSourceOf,
IsRequiredBy,
Requires,
Obsoletes,
IsObsoletedBy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rights {
pub rights: Option<String>,
pub rights_uri: Option<String>,
pub rights_identifier: Option<String>,
pub rights_identifier_scheme: Option<String>,
pub scheme_uri: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Description {
pub description: String,
pub description_type: DescriptionType,
pub lang: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum DescriptionType {
Abstract,
Methods,
SeriesInformation,
TableOfContents,
TechnicalInfo,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeoLocation {
pub geo_location_place: Option<String>,
pub geo_location_point: Option<GeoLocationPoint>,
pub geo_location_box: Option<GeoLocationBox>,
pub geo_location_polygons: Vec<GeoLocationPolygon>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct GeoLocationPoint {
pub point_longitude: f64,
pub point_latitude: f64,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct GeoLocationBox {
pub west_bound_longitude: f64,
pub east_bound_longitude: f64,
pub south_bound_latitude: f64,
pub north_bound_latitude: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeoLocationPolygon {
pub polygon_points: Vec<GeoLocationPoint>,
pub in_polygon_point: Option<GeoLocationPoint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingReference {
pub funder_name: String,
pub funder_identifier: Option<String>,
pub funder_identifier_type: Option<String>,
pub award_number: Option<String>,
pub award_uri: Option<String>,
pub award_title: Option<String>,
}
#[cfg(feature = "xml")]
impl DataCiteMetadata {
pub fn to_xml(&self) -> Result<String> {
use quick_xml::se::to_string;
to_string(&self).map_err(|e| MetadataError::XmlError(e.to_string()))
}
pub fn from_xml(xml: &str) -> Result<Self> {
use quick_xml::de::from_str;
from_str(xml).map_err(|e| MetadataError::XmlError(e.to_string()))
}
}
impl DataCiteMetadata {
pub fn to_json(&self) -> Result<String> {
serde_json::to_string_pretty(&self).map_err(|e| MetadataError::JsonError(e.to_string()))
}
pub fn from_json(json: &str) -> Result<Self> {
serde_json::from_str(json).map_err(|e| MetadataError::JsonError(e.to_string()))
}
}