diff --git a/src/dsp_meta/domain/converter/mod.rs b/src/dsp_meta/domain/converter/mod.rs index 36df4065..62914b0e 100644 --- a/src/dsp_meta/domain/converter/mod.rs +++ b/src/dsp_meta/domain/converter/mod.rs @@ -1 +1,6 @@ pub mod project; + +// Re-exported for convenience. +#[doc(inline)] +pub use project::project_attributes::extract_project_attributes; +pub use project::project_blocks::extract_project_blocks; diff --git a/src/dsp_meta/domain/converter/project/mod.rs b/src/dsp_meta/domain/converter/project/mod.rs index 11d869bf..95e104d3 100644 --- a/src/dsp_meta/domain/converter/project/mod.rs +++ b/src/dsp_meta/domain/converter/project/mod.rs @@ -1,143 +1,2 @@ -use std::collections::HashMap; - -use hcl::Block; - -use crate::domain::converter::project::project_blocks::parse_project_blocks; -use crate::domain::project::Project; -use crate::domain::{AlternativeNames, Description, ID}; -use crate::errors::DspMetaError; - -mod project_attributes; -mod project_blocks; - -pub fn convert_project_block(project_block: &Block) -> Result { - if project_block.identifier.as_str() != "project" { - return Err(crate::errors::DspMetaError::ParseProject( - "Parse error: project block needs to be named 'project'.", - )); - } - - let project_label = project_block.labels().first().ok_or_else(|| { - DspMetaError::ParseProject("Parse error: project needs to have one label.") - })?; - let id = ID(project_label.as_str()); - - // extract the project attributes - // created_at, created_by, shortcode, name, teaser_text, how_to_cite, start_date, end_date, datasets, funders, grants - - let extracted_attributes = - project_attributes::extract_project_attributes(project_block.body.attributes().collect())?; - - let created_at = extracted_attributes.created_at.ok_or_else(|| { - DspMetaError::ParseProject("Parse error: project needs to have a created_at value.") - })?; - - let created_by = extracted_attributes.created_by.ok_or_else(|| { - DspMetaError::ParseProject("Parse error: project needs to have a created_by value.") - })?; - - let shortcode = extracted_attributes.shortcode.ok_or_else(|| { - DspMetaError::ParseProject("Parse error: project needs to have a shortcode.") - })?; - - let name = extracted_attributes - .name - .ok_or_else(|| DspMetaError::ParseProject("Parse error: project needs to have a name."))?; - - let teaser_text = extracted_attributes.teaser_text.ok_or_else(|| { - DspMetaError::ParseProject("Parse error: project needs to have a teaser_text.") - })?; - - let how_to_cite = extracted_attributes.how_to_cite.ok_or_else(|| { - DspMetaError::ParseProject("Parse error: project needs to have a how_to_cite.") - })?; - - let start_date = extracted_attributes.start_date.ok_or_else(|| { - DspMetaError::ParseProject("Parse error: project needs to have a start_date.") - })?; - - let end_date = extracted_attributes.end_date; - - // extract the project blocks - // alternative_names, description, url, keywords, disciplines, publications) - - let project_blocks: Vec<&Block> = project_block.body.blocks().collect(); - let _blocks = parse_project_blocks(project_blocks)?; - - let alternative_names = AlternativeNames(HashMap::new()); - let description = Description(HashMap::new()); - - let project = Project { - id, - created_at, - created_by, - shortcode, - name, - alternative_names, - teaser_text, - description, - how_to_cite, - start_date, - end_date, - }; - - Ok(project) -} - -#[cfg(test)] -mod tests { - use hcl::block; - use tracing_test::traced_test; - - use crate::domain::{ - CreatedAt, CreatedBy, EndDate, HowToCite, Name, Shortcode, StartDate, TeaserText, - }; - - use super::*; - - #[traced_test] - #[test] - fn test_convert_project_block() { - let input_project_block = block!( - project { - created_at = 1630601274523025000u64 // FIXME: is there a more readable way to write an i64? - created_by = "dsp-metadata-gui" - shortcode = "0803" - name = "The German Family Panel (pairfam)" - alternative_name "1" { - de = "Der deutsche Familienpanel (pairfam)" - en = "The German Family Panel (pairfam)" - } - teaser_text = "The German Family Panel (pairfam) is a multidisciplinary, longitudinal study." - description { - de = "Der deutsche Familienpanel (pairfam) ist eine multidisziplinäre, längsschnittliche Studie." - en = "The German Family Panel (pairfam) is a multidisciplinary, longitudinal study." - } - how_to_cite = "Huinink, Johannes; Schröder, Carolin; Castiglioni, Laura; Feldhaus, Michael" - start_date = "2009-04-01" - end_date = "2012-03-31" - } - ); - let project = super::convert_project_block(&input_project_block).unwrap(); - dbg!(&project); - assert_eq!(project.id, ID("0803")); - assert_eq!(project.created_at, CreatedAt(1630601274523025000)); - assert_eq!(project.created_by, CreatedBy("dsp-metadata-gui")); - assert_eq!(project.shortcode, Shortcode("0803")); - assert_eq!(project.name, Name("The German Family Panel (pairfam)")); - assert_eq!( - project.teaser_text, - TeaserText( - "The German Family Panel (pairfam) is a multidisciplinary, longitudinal study." - ) - ); - assert_eq!( - project.how_to_cite, - HowToCite( - "Huinink, Johannes; Schröder, Carolin; Castiglioni, Laura; Feldhaus, Michael" - ) - ); - assert_eq!(project.start_date, StartDate("2009-04-01")); - assert_eq!(project.end_date, Some(EndDate("2012-03-31"))); - } -} +pub mod project_attributes; +pub mod project_blocks; diff --git a/src/dsp_meta/domain/converter/project/project_attributes.rs b/src/dsp_meta/domain/converter/project/project_attributes.rs index a33ad117..e8280c8b 100644 --- a/src/dsp_meta/domain/converter/project/project_attributes.rs +++ b/src/dsp_meta/domain/converter/project/project_attributes.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use hcl::{Attribute, Expression}; use tracing::warn; @@ -8,7 +6,7 @@ use crate::domain::{ }; use crate::errors::DspMetaError; -pub struct ExtractedAttributes<'a> { +struct ExtractedAttributes<'a> { pub created_at: Option, pub created_by: Option>, pub shortcode: Option>, @@ -35,7 +33,7 @@ pub fn extract_project_attributes( match attribute.key() { "created_at" => { created_at = match attribute.expr() { - Expression::Number(value) => Ok(Some(CreatedAt(value.as_u64().unwrap()))), /* FIXME: unwrap */ + Expression::Number(value) => Ok(Some(CreatedAt(value.as_u64().unwrap()))), /* FIXME: get rid of unwrap */ _ => Err(DspMetaError::ParseProject( "Parse error: created_at needs to be a number.", )), diff --git a/src/dsp_meta/domain/converter/project/project_blocks.rs b/src/dsp_meta/domain/converter/project/project_blocks.rs index 5b9973b1..549e518e 100644 --- a/src/dsp_meta/domain/converter/project/project_blocks.rs +++ b/src/dsp_meta/domain/converter/project/project_blocks.rs @@ -1,23 +1,19 @@ -use std::collections::HashMap; - -use hcl::Block; - +use crate::domain::{AlternativeName, AlternativeNames, Description}; use crate::errors::DspMetaError; struct ExtractedProjectBlocks<'a> { - pub alternative_names: Vec, + pub alternative_names: AlternativeNames<'a>, pub description: Option>, } -pub fn parse_project_blocks( - blocks: Vec<&Block>, -) -> Result, DspMetaError> { - let result: HashMap<&str, ProjectValue> = HashMap::new(); - +pub fn extract_project_blocks( + blocks: Vec<&hcl::Block>, +) -> Result { for block in blocks { + let mut alternative_names: Vec<&AlternativeName> = vec![]; + match block.identifier.as_str() { "alternative_name" => { - // TODO: how do we handle multiple alternative names? println!("alternative_name"); dbg!(block); } @@ -31,5 +27,8 @@ pub fn parse_project_blocks( } } } - Ok(result) + Ok(ExtractedProjectBlocks { + alternative_names: AlternativeNames::default(), + description: None, + }) } diff --git a/src/dsp_meta/domain/dataset.rs b/src/dsp_meta/domain/dataset.rs index 97ed726b..26e813a4 100644 --- a/src/dsp_meta/domain/dataset.rs +++ b/src/dsp_meta/domain/dataset.rs @@ -1,27 +1,28 @@ -use serde::{Deserialize, Serialize}; +use crate::domain::{Title, ID}; +use crate::errors::DspMetaError; -#[derive(Debug, PartialEq, Deserialize, Serialize)] -pub struct Dataset { - pub id: String, - pub title: String, +#[derive(Debug, PartialEq)] +pub struct Dataset<'a> { + pub id: ID<'a>, + pub title: Title<'a>, } -impl TryFrom for Dataset { - type Error = crate::errors::DspMetaError; +impl<'a> TryFrom<&hcl::Block> for Dataset<'a> { + type Error = DspMetaError; - fn try_from(dataset_block: hcl::Block) -> Result { + fn try_from(dataset_block: &hcl::Block) -> Result { if dataset_block.identifier.as_str() != "dataset" { - return Err(crate::errors::DspMetaError::ParseDataset( + return Err(DspMetaError::ParseDataset( "Parse error: dataset block needs to be named 'dataset'.", )); } - let id = dataset_block.labels().unwrap().to_string(); - let title = dataset_block - .attributes() - .next() - .unwrap() - .value() - .to_string(); + + let dataset_label = dataset_block.labels().first().ok_or_else(|| { + DspMetaError::ParseDataset("Parse error: dataset needs to have one label.") + })?; + let id = ID(dataset_label.as_str()); + + let title = Title("TODO: implement title extraction"); Ok(Self { id, title }) } } diff --git a/src/dsp_meta/domain/mod.rs b/src/dsp_meta/domain/mod.rs index 09769ca3..10922658 100644 --- a/src/dsp_meta/domain/mod.rs +++ b/src/dsp_meta/domain/mod.rs @@ -1,8 +1,8 @@ -use std::collections::HashMap; +use std::collections::HashSet; use std::fmt::Debug; +use std::hash::Hash; use hcl::Block; -use serde::{Deserialize, Serialize}; use crate::domain::dataset::Dataset; use crate::domain::grant::Grant; @@ -21,29 +21,28 @@ mod project; mod version; /// The Metadata struct represents the metadata of a DSP project. -/// TODO: check if the cardinality of the fields are correct -#[derive(Debug, Default, PartialEq, Deserialize, Serialize)] -pub struct Metadata { - pub version: Version, - pub project: Project, - pub datasets: Vec, +#[derive(Debug, Default, PartialEq)] +pub struct Metadata<'a> { + pub version: &'a Version, + pub project: &'a Project<'a>, + pub datasets: Vec<&'a Dataset<'a>>, pub grants: Vec, pub organizations: Vec, - pub persons: Vec, + pub persons: Vec>, } -impl TryFrom<&hcl::Body> for Metadata { - type Error = crate::errors::DspMetaError; +impl<'a> TryFrom<&hcl::Body> for Metadata<'a> { + type Error = DspMetaError; fn try_from(body: &hcl::Body) -> Result { - let mut version: Option = None; - let mut projects: Vec = vec![]; - let mut datasets: Vec = vec![]; + let mut version: Option<&Version> = None; + let mut project: Option<&Project> = None; + let mut datasets: Vec<&Dataset> = vec![]; let attributes: Vec<&hcl::Attribute> = body.attributes().collect(); for attribute in attributes { match attribute.key() { - "version" => version = Some(Version::try_from(attribute)?), + "version" => version = Some(&Version::try_from(attribute)?), _ => { continue; } @@ -53,8 +52,16 @@ impl TryFrom<&hcl::Body> for Metadata { let blocks: Vec<&Block> = body.blocks().collect(); for block in blocks { match block.identifier() { - "project" => projects.push(Project::try_from(block)?), - "dataset" => datasets.push(Dataset::try_from(block)?), + "project" => { + if project.is_some() { + return Err(DspMetaError::ParseProject( + "Only one project block allowed.", + )); + } else { + project = Some(&Project::try_from(block)?) + } + } + "dataset" => datasets.push(&Dataset::try_from(block)?), _ => { continue; } @@ -64,7 +71,8 @@ impl TryFrom<&hcl::Body> for Metadata { let metadata = Metadata { version: version .ok_or_else(|| DspMetaError::ParseVersion("Version attribute is not provided."))?, - project: Project::try_from(projects)?, + project: project + .ok_or_else(|| DspMetaError::ParseProject("Project block is not provided."))?, datasets: Vec::new(), grants: Vec::new(), organizations: Vec::new(), @@ -74,36 +82,102 @@ impl TryFrom<&hcl::Body> for Metadata { } } -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct ID<'a>(&'a str); -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct CreatedAt(pub u64); -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct CreatedBy<'a>(&'a str); -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Shortcode<'a>(&'a str); -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Name<'a>(&'a str); -/// A HashMap of language codes and their corresponding alternative names. -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] -pub struct AlternativeNames(HashMap); +/// A HashSet of alternative names in different languages. +/// The HashSet is used to ensure that there are no duplicates values in regards +/// to the language code, as LangString only compares the iso_code. +/// TODO: check if this is the correct data structure +#[derive(Debug, Clone, PartialEq)] +pub struct AlternativeNames<'a>(HashSet>); + +impl Default for AlternativeNames<'_> { + fn default() -> Self { + Self::from(vec![ + &AlternativeName { + iso_code: &IsoCode::DE, + string: "Der Default AlternativeName.", + }, + &AlternativeName { + iso_code: &IsoCode::EN, + string: "The default AlternativeName.", + }, + &AlternativeName { + iso_code: &IsoCode::FR, + string: "Le default AlternativeName.", + }, + ]) + } +} + +impl<'a> From>> for AlternativeNames<'_> { + fn from(names: Vec<&AlternativeName>) -> Self { + let mut set = HashSet::new(); + for name in names { + set.insert(LangString { + iso_code: name.iso_code, + string: name.string, + }); + } + Self(set) + } +} -pub struct AlternativeName {} +#[derive(Debug, Clone, PartialEq)] +pub struct AlternativeName<'a> { + iso_code: &'a IsoCode, + string: &'a str, +} /// Represents a string in a specific language. +/// Equality of two language specific strings is ONLY based iso_code. +#[derive(Debug, Clone)] pub struct LangString<'a> { - pub iso_code: IsoCode, + pub iso_code: &'a IsoCode, pub string: &'a str, } +impl Default for LangString<'_> { + fn default() -> Self { + Self { + iso_code: &IsoCode::DE, + string: "Der Default LangString.", + } + } +} + +impl Hash for LangString<'_> { + fn hash(&self, state: &mut H) { + self.iso_code.hash(state); + } +} + +impl PartialEq for LangString<'_> { + fn eq(&self, other: &Self) -> bool { + self.iso_code == other.iso_code + } +} + +impl Eq for LangString<'_> {} + /// Language codes according to ISO 639-1 /// Not an exhaustive list. +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] enum IsoCode { + #[default] DE, // German EN, // English FR, // French @@ -119,28 +193,63 @@ enum IsoCode { FA, // Persian } -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct TeaserText<'a>(&'a str); -/// A HashMap of language codes and their corresponding descriptions. -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] -pub struct Description(HashMap); +/// A set of descriptions in different languages. +#[derive(Debug, Clone, PartialEq)] +pub struct Description<'a>(HashSet>); + +impl Default for Description<'_> { + fn default() -> Self { + Self::from(vec![ + &LangString { + iso_code: &IsoCode::DE, + string: "Die default Beschreibung.", + }, + &LangString { + iso_code: &IsoCode::EN, + string: "The default description.", + }, + &LangString { + iso_code: &IsoCode::FR, + string: "Le standard description.", + }, + ]) + } +} -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +impl From>> for Description<'_> { + fn from(descriptions: Vec<&LangString>) -> Self { + let mut set = HashSet::new(); + for description in descriptions { + set.insert(LangString { + iso_code: description.iso_code, + string: description.string, + }); + } + Self(set) + } +} + +#[derive(Debug, Default, Clone, PartialEq)] pub struct HowToCite<'a>(&'a str); -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct StartDate<'a>(&'a str); -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct EndDate<'a>(&'a str); -#[derive(Debug, Default, PartialEq, Deserialize, Serialize)] -pub struct Datasets(Vec); +#[derive(Debug, Default, PartialEq)] +pub struct Datasets<'a>(Vec<&'a Dataset<'a>>); -#[derive(Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Default, PartialEq)] pub struct Grants(Vec); +#[derive(Debug, Default, PartialEq)] +pub struct Title<'a>(&'a str); + #[cfg(test)] mod tests { use hcl::body; diff --git a/src/dsp_meta/domain/person.rs b/src/dsp_meta/domain/person.rs index 4840031a..ff30a75f 100644 --- a/src/dsp_meta/domain/person.rs +++ b/src/dsp_meta/domain/person.rs @@ -1,12 +1,4 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Deserialize, Serialize)] -pub struct Person { - id: String, -} - -impl From<&str> for Person { - fn from(id: &str) -> Self { - Self { id: id.to_string() } - } +#[derive(Debug, PartialEq)] +pub struct Person<'a> { + id: &'a str, } diff --git a/src/dsp_meta/domain/project.rs b/src/dsp_meta/domain/project.rs index 5ac5f801..495e8b3c 100644 --- a/src/dsp_meta/domain/project.rs +++ b/src/dsp_meta/domain/project.rs @@ -1,6 +1,5 @@ -use serde::{Deserialize, Serialize}; - -use crate::domain::converter::project::convert_project_block; +use crate::domain::converter::project::project_attributes; +use crate::domain::converter::project::project_blocks::extract_project_blocks; use crate::domain::{ AlternativeNames, CreatedAt, CreatedBy, Description, EndDate, HowToCite, Name, Shortcode, StartDate, TeaserText, ID, @@ -8,47 +7,159 @@ use crate::domain::{ use crate::errors::DspMetaError; // no need for smart constructors here, as there is no validation happening -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)] -pub struct Project { - pub id: ID, +#[derive(Debug, Default, Clone, PartialEq)] +pub struct Project<'a> { + pub id: ID<'a>, pub created_at: CreatedAt, - pub created_by: CreatedBy, - pub shortcode: Shortcode, - pub name: Name, - pub alternative_names: AlternativeNames, - pub teaser_text: TeaserText, - pub description: Description, - pub how_to_cite: HowToCite, - pub start_date: StartDate, - pub end_date: Option, + pub created_by: CreatedBy<'a>, + pub shortcode: Shortcode<'a>, + pub name: Name<'a>, + pub alternative_names: AlternativeNames<'a>, + pub teaser_text: TeaserText<'a>, + pub description: Description<'a>, + pub how_to_cite: HowToCite<'a>, + pub start_date: StartDate<'a>, + pub end_date: Option>, } -impl TryFrom<&hcl::Block> for Project { - type Error = crate::errors::DspMetaError; +impl<'a> TryFrom<&hcl::Block> for Project<'a> { + type Error = DspMetaError; fn try_from(project_block: &hcl::Block) -> Result { - convert_project_block(project_block) + if project_block.identifier.as_str() != "project" { + return Err(DspMetaError::ParseProject( + "Parse error: project block needs to be named 'project'.", + )); + } + + let project_label = project_block.labels().first().ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have one label.") + })?; + let id = ID(project_label.as_str()); + + // extract the project attributes + // created_at, created_by, shortcode, name, teaser_text, how_to_cite, start_date, end_date, datasets, funders, grants + + let attributes = project_block.body.attributes().collect(); + + let extracted_attributes = project_attributes::extract_project_attributes(attributes)?; + + let created_at = extracted_attributes.created_at.ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have a created_at value.") + })?; + + let created_by = extracted_attributes.created_by.ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have a created_by value.") + })?; + + let shortcode = extracted_attributes.shortcode.ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have a shortcode.") + })?; + + let name = extracted_attributes.name.ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have a name.") + })?; + + let teaser_text = extracted_attributes.teaser_text.ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have a teaser_text.") + })?; + + let how_to_cite = extracted_attributes.how_to_cite.ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have a how_to_cite.") + })?; + + let start_date = extracted_attributes.start_date.ok_or_else(|| { + DspMetaError::ParseProject("Parse error: project needs to have a start_date.") + })?; + + let end_date = extracted_attributes.end_date; + + // extract the project blocks + // alternative_names, description, url, keywords, disciplines, publications) + + let blocks: Vec<&hcl::Block> = project_block.body.blocks().collect(); + let _extracted_blocks = extract_project_blocks(blocks)?; + + let alternative_names = AlternativeNames::default(); + let description = Description::default(); + + let project = Project { + id, + created_at, + created_by, + shortcode, + name, + alternative_names, + teaser_text, + description, + how_to_cite, + start_date, + end_date, + }; + + Ok(project) } } -/// Given potentially a list of projects, flatten to one project. -/// Our constraint is that there must be exactly one project defined per metadata file. -impl TryFrom> for Project { - type Error = crate::errors::DspMetaError; +impl Default for &Project<'_> { + fn default() -> Self { + &Project::default() + } +} - fn try_from(projects: Vec) -> Result { - if projects.len() > 1 { - return Err(DspMetaError::ParseProject( - "There can only be one project block.", - )); - } +#[cfg(test)] +mod tests { + use hcl::block; + use tracing_test::traced_test; - if projects.is_empty() { - return Err(DspMetaError::ParseProject( - "There needs to be a project block.", - )); - } + use super::*; + use crate::domain::{ + CreatedAt, CreatedBy, EndDate, HowToCite, Name, Shortcode, StartDate, TeaserText, + }; - Ok(projects[0].clone()) + #[traced_test] + #[test] + fn test_convert_project_block() { + let input_project_block = block!( + project { + created_at = 1630601274523025000u64 // FIXME: is there a more readable way to write an i64? + created_by = "dsp-metadata-gui" + shortcode = "0803" + name = "The German Family Panel (pairfam)" + alternative_name "1" { + de = "Der deutsche Familienpanel (pairfam)" + en = "The German Family Panel (pairfam)" + } + teaser_text = "The German Family Panel (pairfam) is a multidisciplinary, longitudinal study." + description { + de = "Der deutsche Familienpanel (pairfam) ist eine multidisziplinäre, längsschnittliche Studie." + en = "The German Family Panel (pairfam) is a multidisciplinary, longitudinal study." + } + how_to_cite = "Huinink, Johannes; Schröder, Carolin; Castiglioni, Laura; Feldhaus, Michael" + start_date = "2009-04-01" + end_date = "2012-03-31" + } + ); + let project = Project::try_from(&input_project_block).unwrap(); + dbg!(&project); + assert_eq!(project.id, ID("0803")); + assert_eq!(project.created_at, CreatedAt(1630601274523025000)); + assert_eq!(project.created_by, CreatedBy("dsp-metadata-gui")); + assert_eq!(project.shortcode, Shortcode("0803")); + assert_eq!(project.name, Name("The German Family Panel (pairfam)")); + assert_eq!( + project.teaser_text, + TeaserText( + "The German Family Panel (pairfam) is a multidisciplinary, longitudinal study." + ) + ); + assert_eq!( + project.how_to_cite, + HowToCite( + "Huinink, Johannes; Schröder, Carolin; Castiglioni, Laura; Feldhaus, Michael" + ) + ); + assert_eq!(project.start_date, StartDate("2009-04-01")); + assert_eq!(project.end_date, Some(EndDate("2012-03-31"))); } } diff --git a/src/dsp_meta/domain/version.rs b/src/dsp_meta/domain/version.rs index 2bbb8a75..67a90a63 100644 --- a/src/dsp_meta/domain/version.rs +++ b/src/dsp_meta/domain/version.rs @@ -1,7 +1,6 @@ use hcl::{Attribute, Expression}; -use serde::{Deserialize, Serialize}; -#[derive(Debug, Default, PartialEq, Deserialize, Serialize)] +#[derive(Debug, PartialEq)] pub struct Version(pub u64); /// Given a list of attributes, try to extract the version. @@ -24,6 +23,12 @@ impl TryFrom<&Attribute> for Version { } } +impl<'a> Default for &'a Version { + fn default() -> Self { + &Version(1) + } +} + #[cfg(test)] mod tests { diff --git a/src/dsp_meta/lib.rs b/src/dsp_meta/lib.rs index 7444e895..f8381d73 100644 --- a/src/dsp_meta/lib.rs +++ b/src/dsp_meta/lib.rs @@ -8,7 +8,7 @@ use domain::Metadata; use crate::errors::DspMetaError; -pub fn load>(path: P) -> Result { +pub fn load<'a, P: AsRef>(path: P) -> Result, DspMetaError> { let input = std::fs::read_to_string(path)?; let body: hcl::Body = hcl::from_str(&input)?; let metadata = Metadata::try_from(&body)?;