From 9025a1da053f13636bbe84f184b0aa3830d60fc2 Mon Sep 17 00:00:00 2001 From: Ivan Subotic <400790+subotic@users.noreply.github.com> Date: Thu, 14 Sep 2023 17:49:22 +0200 Subject: [PATCH] add conversion of project - discipline (ongoing) --- src/dsp_meta/domain/convert/project.rs | 97 +++++++++++++++++++++---- src/dsp_meta/domain/entity/project.rs | 5 +- src/dsp_meta/domain/value/discipline.rs | 51 +++++++++++++ src/dsp_meta/domain/value/iso_code.rs | 67 +++++++++++++++++ src/dsp_meta/domain/value/mod.rs | 83 +-------------------- 5 files changed, 207 insertions(+), 96 deletions(-) create mode 100644 src/dsp_meta/domain/value/discipline.rs create mode 100644 src/dsp_meta/domain/value/iso_code.rs diff --git a/src/dsp_meta/domain/convert/project.rs b/src/dsp_meta/domain/convert/project.rs index 586e71d6..4d85d833 100644 --- a/src/dsp_meta/domain/convert/project.rs +++ b/src/dsp_meta/domain/convert/project.rs @@ -1,7 +1,14 @@ +use clap::builder::Str; use hcl::Expression; +use std::collections::HashMap; use tracing::warn; -use crate::domain::value::{AlternativeName, ContactPoint, CreatedAt, CreatedBy, Description, Discipline, EndDate, HowToCite, IsoCode, Keyword, LangString, Name, Publication, Shortcode, StartDate, TeaserText, URL}; +use crate::domain::value::discipline::{Discipline, DisciplineData}; +use crate::domain::value::iso_code::IsoCode; +use crate::domain::value::{ + AlternativeName, ContactPoint, CreatedAt, CreatedBy, Description, EndDate, HowToCite, Keyword, + LangString, Name, Publication, Shortcode, StartDate, TeaserText, URL, +}; use crate::errors::DspMetaError; pub struct ExtractedProjectAttributes { @@ -333,26 +340,86 @@ impl TryFrom<&hcl::Block> for Discipline { return Err(DspMetaError::CreateValueObject(msg)); } - if block.labels.len() != 2 { - return Err(DspMetaError::CreateValueObject("The passed number of block labels is not correct. Expected '2', namely 'vocabulary' and 'id'.".to_string())); + if block.labels.len() != 1 { + return Err(DspMetaError::CreateValueObject("The passed number of block labels is not correct. Expected '1', namely 'reference data type' (e.g., 'skos').".to_string())); } - let vocabulary = block.labels.get(0).ok_or_else(|| { + let reference_data_type = block.labels.first().ok_or_else(|| { DspMetaError::CreateValueObject( - "The passed discipline block is missing the vocabulary label.".to_string(), + "The passed discipline block is missing the reference data type label.".to_string(), ) })?; - let id = block.labels.get(1).ok_or_else(|| { - DspMetaError::CreateValueObject( - "The passed discipline block is missing the id label.".to_string(), - ) - })?; - - Ok(Discipline { - vocabulary: vocabulary.to_owned(), - id: id.to_owned(), - }) + match reference_data_type.as_str() { + "skos" || "snf" => { + let mut ref_id: Option = None; + let mut description: Option = None; + let mut url: Option = None; + let attrs: Vec<&hcl::Attribute> = block.body.attributes().collect(); + for attr in attrs { + match attr.key() { + "ref_id" => { + ref_id = match attr.expr() { + Expression::String(value) => Ok(Some(value.to_owned())), + _ => Err(DspMetaError::CreateValueObject( + "The passed discipline block ref_id attribute is not of String type.".to_string(), + )), + }?; + } + "description" => { + description = match attr.expr() { + Expression::String(value) => Ok(Some(value.to_owned())), + _ => Err(DspMetaError::CreateValueObject( + "The passed discipline block description attribute is not of String type.".to_string(), + )), + }?; + } + "url" => { + url = match attr.expr() { + Expression::String(value) => Ok(Some(url::Url::parse(value).map_err(|_| { + DspMetaError::CreateValueObject("The passed discipline block url attribute is not a valid url.".to_string()) + })?)), + _ => Err(DspMetaError::CreateValueObject( + "The passed discipline block url attribute is not of String type.".to_string(), + )), + }?; + } + _ => { + warn!("Parse error: unknown attribute '{}'.", attr.key()); + } + } + } + Ok(Discipline::Skos { + ref_id: ref_id.ok_or_else(|| { + DspMetaError::CreateValueObject( + "The passed discipline block is missing the ref_id attribute.".to_string(), + ) + })?, + description: description.ok_or_else(|| { + DspMetaError::CreateValueObject( + "The passed discipline block is missing the description attribute.".to_string(), + ) + })?, + url: url.ok_or_else(|| { + DspMetaError::CreateValueObject( + "The passed discipline block is missing the url attribute.".to_string(), + ) + })?, + }) + } + "text" => { + let mut descriptions: HashMap = HashMap::new(); + let attrs: Vec<&hcl::Attribute> = block.body.attributes().collect(); + for attr in attrs { + let lang_string = LangString::try_from(attr)?; + descriptions.insert(lang_string.iso_code, lang_string.string); + } + Ok(Discipline::Text(descriptions)) + } + _ => { + Err(DspMetaError::CreateValueObject("The passed discipline block is missing the correct reference data type label: 'skos', 'snf', or 'text'.".to_string())) + } + } } } diff --git a/src/dsp_meta/domain/entity/project.rs b/src/dsp_meta/domain/entity/project.rs index 5a5275f0..b31299ff 100644 --- a/src/dsp_meta/domain/entity/project.rs +++ b/src/dsp_meta/domain/entity/project.rs @@ -1,7 +1,8 @@ use crate::domain::convert::project::{ExtractedProjectAttributes, ExtractedProjectBlocks}; +use crate::domain::value::discipline::Discipline; use crate::domain::value::{ - AlternativeName, ContactPoint, CreatedAt, CreatedBy, Description, Discipline, EndDate, - HowToCite, Keyword, Name, Publication, Shortcode, StartDate, TeaserText, URL, + AlternativeName, ContactPoint, CreatedAt, CreatedBy, Description, EndDate, HowToCite, Keyword, + Name, Publication, Shortcode, StartDate, TeaserText, URL, }; use crate::errors::DspMetaError; diff --git a/src/dsp_meta/domain/value/discipline.rs b/src/dsp_meta/domain/value/discipline.rs new file mode 100644 index 00000000..8bc92beb --- /dev/null +++ b/src/dsp_meta/domain/value/discipline.rs @@ -0,0 +1,51 @@ +use crate::domain::value::iso_code::IsoCode; +use crate::domain::value::LangString; +use hcl::Attribute; +use std::collections::HashMap; + +/// The discipline of a project can be defined in two ways: +/// 1. A reference to a discipline defined in an external reference system (e.g. SNF or SKOS) +/// 2. A text description of the discipline +/// +/// Example: +/// ```hcl +/// discipline skos { +/// ref_id = "https://skos.um.es/unesco6/5501" +/// description = "Local history" +/// url = "https://skos.um.es/unesco6/5501" +/// } +/// ``` +/// would be represented as: +/// ```rust +/// use dsp_meta::domain::value::Discipline; +/// use dsp_meta::domain::value::Ref; +/// use std::collections::HashMap; +/// use url::Url; +/// Discipline::Snf(Ref{ +/// ref_id: "https://skos.um.es/unesco6/5501".to_string(), +/// description: "Local history".to_string(), +/// url: Url::parse("https://skos.um.es/unesco6/5501").unwrap(), +/// }) +/// ``` +#[derive(Debug, PartialEq)] +pub enum Discipline { + Skos { + ref_id: String, + description: String, + url: url::Url, + }, + Snf { + ref_id: String, + description: String, + url: url::Url, + }, + Text(HashMap), +} + +impl TryFrom> for Discipline { + type Error = (); + + fn try_from(value: Vec<&Attribute>) -> Result { + todo!() + } +} diff --git a/src/dsp_meta/domain/value/iso_code.rs b/src/dsp_meta/domain/value/iso_code.rs new file mode 100644 index 00000000..8c5e65ed --- /dev/null +++ b/src/dsp_meta/domain/value/iso_code.rs @@ -0,0 +1,67 @@ +use crate::errors::DspMetaError; +use std::fmt::{Display, Formatter}; + +/// Language codes according to ISO 639-1 +/// Not an exhaustive list. +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] +pub enum IsoCode { + #[default] + DE, // German + EN, // English + FR, // French + IT, // Italian + ES, // Spanish + PT, // Portuguese + NL, // Dutch + PL, // Polish + RU, // Russian + JA, // Japanese + ZH, // Chinese + AR, // Arabic + FA, // Persian +} + +impl Display for IsoCode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + IsoCode::DE => write!(f, "de"), + IsoCode::EN => write!(f, "en"), + IsoCode::FR => write!(f, "fr"), + IsoCode::IT => write!(f, "it"), + IsoCode::ES => write!(f, "es"), + IsoCode::PT => write!(f, "pt"), + IsoCode::NL => write!(f, "nl"), + IsoCode::PL => write!(f, "pl"), + IsoCode::RU => write!(f, "ru"), + IsoCode::JA => write!(f, "ja"), + IsoCode::ZH => write!(f, "zh"), + IsoCode::AR => write!(f, "ar"), + IsoCode::FA => write!(f, "fa"), + } + } +} + +impl TryFrom<&str> for IsoCode { + type Error = DspMetaError; + + fn try_from(value: &str) -> Result { + match value { + "de" => Ok(IsoCode::DE), + "en" => Ok(IsoCode::EN), + "fr" => Ok(IsoCode::FR), + "it" => Ok(IsoCode::IT), + "es" => Ok(IsoCode::ES), + "pt" => Ok(IsoCode::PT), + "nl" => Ok(IsoCode::NL), + "pl" => Ok(IsoCode::PL), + "ru" => Ok(IsoCode::RU), + "ja" => Ok(IsoCode::JA), + "zh" => Ok(IsoCode::ZH), + "ar" => Ok(IsoCode::AR), + "fa" => Ok(IsoCode::FA), + _ => Err(DspMetaError::CreateValueObject( + "Creating an IsoCode failed because provided value is not allowed.".to_string(), + )), + } + } +} diff --git a/src/dsp_meta/domain/value/mod.rs b/src/dsp_meta/domain/value/mod.rs index 298985cd..4bb05596 100644 --- a/src/dsp_meta/domain/value/mod.rs +++ b/src/dsp_meta/domain/value/mod.rs @@ -1,7 +1,10 @@ use crate::errors::DspMetaError; +use iso_code::IsoCode; use std::collections::HashMap; -use std::fmt::{Display, Formatter}; +use std::fmt::Display; +pub(crate) mod discipline; +mod iso_code; pub(crate) mod version; #[derive(Debug, Default, Clone, PartialEq)] @@ -72,71 +75,6 @@ impl Default for LangString { } } -/// Language codes according to ISO 639-1 -/// Not an exhaustive list. -#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] -pub enum IsoCode { - #[default] - DE, // German - EN, // English - FR, // French - IT, // Italian - ES, // Spanish - PT, // Portuguese - NL, // Dutch - PL, // Polish - RU, // Russian - JA, // Japanese - ZH, // Chinese - AR, // Arabic - FA, // Persian -} - -impl Display for IsoCode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - IsoCode::DE => write!(f, "de"), - IsoCode::EN => write!(f, "en"), - IsoCode::FR => write!(f, "fr"), - IsoCode::IT => write!(f, "it"), - IsoCode::ES => write!(f, "es"), - IsoCode::PT => write!(f, "pt"), - IsoCode::NL => write!(f, "nl"), - IsoCode::PL => write!(f, "pl"), - IsoCode::RU => write!(f, "ru"), - IsoCode::JA => write!(f, "ja"), - IsoCode::ZH => write!(f, "zh"), - IsoCode::AR => write!(f, "ar"), - IsoCode::FA => write!(f, "fa"), - } - } -} - -impl TryFrom<&str> for IsoCode { - type Error = DspMetaError; - - fn try_from(value: &str) -> Result { - match value { - "de" => Ok(IsoCode::DE), - "en" => Ok(IsoCode::EN), - "fr" => Ok(IsoCode::FR), - "it" => Ok(IsoCode::IT), - "es" => Ok(IsoCode::ES), - "pt" => Ok(IsoCode::PT), - "nl" => Ok(IsoCode::NL), - "pl" => Ok(IsoCode::PL), - "ru" => Ok(IsoCode::RU), - "ja" => Ok(IsoCode::JA), - "zh" => Ok(IsoCode::ZH), - "ar" => Ok(IsoCode::AR), - "fa" => Ok(IsoCode::FA), - _ => Err(DspMetaError::CreateValueObject( - "Creating an IsoCode failed because provided value is not allowed.".to_string(), - )), - } - } -} - #[derive(Debug, Default, Clone, PartialEq)] pub struct TeaserText(pub String); @@ -241,19 +179,6 @@ pub struct ContactPoint(pub String); #[derive(Debug, Default, PartialEq)] pub struct Title(pub String); -#[derive(Debug, Default, PartialEq)] -pub struct Discipline { - pub discipline_type: DisciplineType, - pub description: LangString, - pub url: URL, -} - -#[derive(Debug, Default, PartialEq)] -pub enum DisciplineType { - #[default] - Snf, -} - #[derive(Debug, PartialEq)] pub struct Publication(String);