Skip to content

Commit

Permalink
refactor(dsp-meta): conversion of domain entities / values
Browse files Browse the repository at this point in the history
  • Loading branch information
subotic committed Nov 7, 2023
1 parent 6262e7a commit a386725
Show file tree
Hide file tree
Showing 32 changed files with 245 additions and 132 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dsp-domain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ tracing.workspace = true
tracing-subscriber.workspace = true
tracing-test.workspace = true
url.workspace = true
thiserror = "1.0.50"

[dev-dependencies]
assert_cmd = "2.0.12"
14 changes: 14 additions & 0 deletions dsp-domain/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use thiserror::Error;

/// Type alias for `Result` with default error `DspDomainError`.
///
/// Can be used like `std::result::Result` as well.
pub type Result<T, E = DspDomainError> = std::result::Result<T, E>;

/// This error is raised when a domain entity or value fails creation
/// at runtime.
#[derive(Debug, Error)]
pub enum DspDomainError {
#[error("Error creating value object: `{0}`")]
CreateValueObject(String),
}
1 change: 1 addition & 0 deletions dsp-domain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod error;
pub mod metadata;
7 changes: 7 additions & 0 deletions dsp-domain/src/metadata/value/alternative_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use serde::Serialize;

use crate::metadata::value::iso_code::IsoCode;
use crate::metadata::value::lang_text_data::LangTextData;

#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct AlternativeName(pub HashMap<IsoCode, String>);
Expand All @@ -16,3 +17,9 @@ impl Default for AlternativeName {
Self(map)
}
}

impl From<LangTextData> for AlternativeName {
fn from(value: LangTextData) -> Self {
AlternativeName(value.0)
}
}
7 changes: 7 additions & 0 deletions dsp-domain/src/metadata/value/description.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use serde::Serialize;

use crate::metadata::value::iso_code::IsoCode;
use crate::metadata::value::lang_text_data::LangTextData;

/// A set of descriptions in different languages.
#[derive(Debug, Clone, PartialEq, Serialize)]
Expand All @@ -17,3 +18,9 @@ impl Default for Description {
Self(map)
}
}

impl From<LangTextData> for Description {
fn from(value: LangTextData) -> Self {
Description(value.0)
}
}
10 changes: 6 additions & 4 deletions dsp-domain/src/metadata/value/iso_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::fmt::{Display, Formatter};

use serde::Serialize;

use crate::error::DspDomainError;

/// Language codes according to ISO 639-1
/// Not an exhaustive list.
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize)]
Expand Down Expand Up @@ -44,7 +46,7 @@ impl Display for IsoCode {
}

impl TryFrom<&str> for IsoCode {
type Error = String;
type Error = DspDomainError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
Expand All @@ -61,9 +63,9 @@ impl TryFrom<&str> for IsoCode {
"zh" => Ok(IsoCode::ZH),
"ar" => Ok(IsoCode::AR),
"fa" => Ok(IsoCode::FA),
_ => {
Err("Creating an IsoCode failed because provided value is not allowed.".to_string())
}
_ => Err(DspDomainError::CreateValueObject(
"Creating an IsoCode failed because provided value is not allowed.".to_string(),
)),
}
}
}
7 changes: 7 additions & 0 deletions dsp-domain/src/metadata/value/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ use std::collections::HashMap;
use serde::Serialize;

use crate::metadata::value::iso_code::IsoCode;
use crate::metadata::value::lang_text_data::LangTextData;

#[derive(Clone, Debug, Default, PartialEq, Serialize)]
pub struct Keyword(pub HashMap<IsoCode, String>);

impl From<LangTextData> for Keyword {
fn from(value: LangTextData) -> Self {
Keyword(value.0)
}
}
1 change: 1 addition & 0 deletions dsp-domain/src/metadata/value/publication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ use crate::metadata::value::simple_text_data::SimpleTextData;
pub enum Publication {
SimpleText(SimpleTextData),
}

8 changes: 8 additions & 0 deletions dsp-domain/src/metadata/value/simple_text_data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
use serde::Serialize;

use crate::metadata::value::publication::Publication;

#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct SimpleTextData(pub String);

impl SimpleTextData {
pub fn into_simple_text(self) -> Publication {
Publication::SimpleText(self)
}
}
12 changes: 3 additions & 9 deletions dsp-meta/src/api/convert/axum/project_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@ use dsp_domain::metadata::entity::project_metadata::ProjectMetadata;
use serde::Serialize;

#[derive(Debug, Default, Clone, PartialEq, Serialize)]
pub struct OptionalProjectMetadata(pub Option<ProjectMetadata>);
pub struct ProjectMetadataDto(pub Option<ProjectMetadata>);

impl IntoResponse for ProjectMetadata {
fn into_response(self) -> Response {
(StatusCode::OK, Json(serde_json::to_value(self).unwrap())).into_response()
}
}

impl IntoResponse for OptionalProjectMetadata {
impl IntoResponse for ProjectMetadataDto {
fn into_response(self) -> Response {
match self.0 {
Some(result) => result.into_response(),
Some(pm) => (StatusCode::OK, Json(serde_json::to_value(pm).unwrap())).into_response(),
None => (StatusCode::NOT_FOUND).into_response(),
}
}
Expand Down
13 changes: 7 additions & 6 deletions dsp-meta/src/api/convert/hcl/dataset.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
use dsp_domain::metadata::entity::dataset::Dataset;
use dsp_domain::metadata::value::Title;

use crate::api::convert::hcl::hcl_block::HclBlock;
use crate::error::DspMetaError;

impl TryFrom<&hcl::Block> for Dataset {
impl<'a> TryInto<Dataset> for HclBlock<'a> {
type Error = DspMetaError;

fn try_from(dataset_block: &hcl::Block) -> Result<Self, Self::Error> {
if dataset_block.identifier.as_str() != "dataset" {
fn try_into(self) -> Result<Dataset, Self::Error> {
if self.0.identifier.as_str() != "dataset" {
return Err(DspMetaError::ParseDataset(
format!(
"Parse error: dataset block needs to be named 'dataset', however got '{}' instead.",
dataset_block.identifier.as_str()
self.0.identifier.as_str()
)
.to_string(),
.to_string(),
));
}

let title = Title(String::from("TODO: implement title extraction"));
Ok(Self { title })
Ok(Dataset { title })
}
}
21 changes: 8 additions & 13 deletions dsp-meta/src/api/convert/hcl/extracted_project_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,28 @@ impl TryFrom<Vec<&hcl::Block>> for ExtractedProjectBlocks {

for block in blocks {
match block.identifier.as_str() {
ALTERNATIVE_NAME_BLOCK => {
// alternative_names.push(AlternativeName::try_from(block)?);
alternative_names.push(HclBlock(block).try_into()?)
}
ALTERNATIVE_NAME_BLOCK => alternative_names.push(HclBlock(block).try_into()?),
DESCRIPTION_BLOCK => {
if description.is_some() {
return Err(DspMetaError::ParseProject(
"Only one 'description' block allowed.".to_string(),
));
}
description = Some(Description::try_from(block)?)
description = Some(HclBlock(&block).try_into()?)
}
URL_BLOCK => {
if url.is_some() {
return Err(DspMetaError::ParseProject(
"Only one 'url' block allowed.".to_string(),
));
}
url = Some(Url::try_from(block)?)
}
KEYWORD_BLOCK => keywords.push(Keyword::try_from(block)?),
DISCIPLINE_BLOCK => disciplines.push(Discipline::try_from(block)?),
SPACIAL_COVERAGE_BLOCK => spacial_coverages.push(SpacialCoverage::try_from(block)?),
TEMPORAL_COVERAGE_BLOCK => {
temporal_coverages.push(TemporalCoverage::try_from(block)?)
url = Some(HclBlock(&block).try_into()?)
}
PUBLICATION_BLOCK => publications.push(Publication::try_from(block)?),
KEYWORD_BLOCK => keywords.push(HclBlock(&block).try_into()?),
DISCIPLINE_BLOCK => disciplines.push(HclBlock(&block).try_into()?),
SPACIAL_COVERAGE_BLOCK => spacial_coverages.push(HclBlock(&block).try_into()?),
TEMPORAL_COVERAGE_BLOCK => temporal_coverages.push(HclBlock(&block).try_into()?),
PUBLICATION_BLOCK => publications.push(HclBlock(&block).try_into()?),
_ => {
// catch all
warn!("Parse error: unknown block '{}'.", block.identifier);
Expand Down
2 changes: 2 additions & 0 deletions dsp-meta/src/api/convert/hcl/hcl_attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub struct HclAttribute<'a>(pub &'a hcl::Attribute);
pub struct HclAttributes<'a>(pub Vec<&'a hcl::Attribute>);
1 change: 1 addition & 0 deletions dsp-meta/src/api/convert/hcl/hcl_body.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct HclBody<'a>(pub &'a hcl::Block);
4 changes: 3 additions & 1 deletion dsp-meta/src/api/convert/hcl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ mod dataset;
mod dsp_meta_error;
mod extracted_project_attributes;
mod extracted_project_blocks;
pub mod hcl_attribute;
mod hcl_block;
pub mod hcl_body;
mod project;
pub(crate) mod project_metadata;
mod value;
mod hcl_block;
15 changes: 8 additions & 7 deletions dsp-meta/src/api/convert/hcl/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ use dsp_domain::metadata::value::url::Url;

use crate::api::convert::hcl::extracted_project_attributes::ExtractedProjectAttributes;
use crate::api::convert::hcl::extracted_project_blocks::ExtractedProjectBlocks;
use crate::api::convert::hcl::hcl_block::HclBlock;
use crate::error::DspMetaError;

impl TryFrom<&hcl::Block> for Project {
impl<'a> TryInto<Project> for HclBlock<'a> {
type Error = DspMetaError;

fn try_from(project_block: &hcl::Block) -> Result<Self, Self::Error> {
if project_block.identifier.as_str() != "project" {
fn try_into(self) -> Result<Project, Self::Error> {
if self.0.identifier.as_str() != "project" {
return Err(DspMetaError::ParseProject(
format!(
"Parse error: project block needs to be named 'project', however got '{}' instead.",
project_block.identifier.as_str()
self.0.identifier.as_str()
)
.to_string(),
));
Expand All @@ -23,7 +24,7 @@ impl TryFrom<&hcl::Block> for Project {
// created_at, created_by, shortcode, name, teaser_text, how_to_cite, start_date, end_date,
// datasets, funders, grants

let attributes: Vec<&hcl::Attribute> = project_block.body.attributes().collect();
let attributes: Vec<&hcl::Attribute> = self.0.body.attributes().collect();

let extracted_attributes = ExtractedProjectAttributes::try_from(attributes)?;

Expand Down Expand Up @@ -73,7 +74,7 @@ impl TryFrom<&hcl::Block> for Project {
// extract the project blocks
// alternative_names, description, url, keywords, disciplines, publications)

let blocks: Vec<&hcl::Block> = project_block.body.blocks().collect();
let blocks: Vec<&hcl::Block> = self.0.body.blocks().collect();
let extracted_blocks = ExtractedProjectBlocks::try_from(blocks)?;

let alternative_names = extracted_blocks.alternative_names;
Expand Down Expand Up @@ -144,7 +145,7 @@ mod tests {
contact_point = "project_organization"
}
);
let project = Project::try_from(&input_project_block).unwrap();
let project: Project = HclBlock(&input_project_block).try_into().unwrap();
assert_eq!(project.created_at, CreatedAt(1630601274523025000));
assert_eq!(
project.created_by,
Expand Down
17 changes: 10 additions & 7 deletions dsp-meta/src/api/convert/hcl/project_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@ use dsp_domain::metadata::entity::project::Project;
use dsp_domain::metadata::entity::project_metadata::ProjectMetadata;
use dsp_domain::metadata::value::version::Version;

use crate::api::convert::hcl::hcl_attribute::HclAttribute;
use crate::api::convert::hcl::hcl_block::HclBlock;
use crate::api::convert::hcl::hcl_body::HclBody;
use crate::error::DspMetaError;

impl TryFrom<&hcl::Body> for ProjectMetadata {
impl<'a> TryInto<ProjectMetadata> for HclBody<'a> {
type Error = DspMetaError;

/// Converts an `hcl::Body` into `ProjectMetadata` by consuming the
/// input. This operation can fail.
fn try_from(body: &hcl::Body) -> Result<Self, Self::Error> {
fn try_into(self) -> Result<ProjectMetadata, Self::Error> {
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();
let attributes: Vec<&hcl::Attribute> = self.0.body.attributes().collect();
for attribute in attributes {
match attribute.key() {
"version" => version = Some(Version::try_from(attribute)?),
"version" => version = Some(HclAttribute(attribute).try_into()?),
_ => {
continue;
}
}
}

let blocks: Vec<&hcl::Block> = body.blocks().collect();
let blocks: Vec<&hcl::Block> = self.0.body.blocks().collect();
for block in blocks {
match block.identifier() {
"project" => {
Expand All @@ -34,10 +37,10 @@ impl TryFrom<&hcl::Body> for ProjectMetadata {
"Only one project block allowed.".to_string(),
));
} else {
project = Some(Project::try_from(block)?)
project = Some(HclBlock(block).try_into()?)
}
}
"dataset" => datasets.push(Dataset::try_from(block)?),
"dataset" => datasets.push(HclBlock(block).try_into()?),
_ => {
continue;
}
Expand Down
9 changes: 8 additions & 1 deletion dsp-meta/src/api/convert/hcl/value/alternative_name.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use dsp_domain::metadata::value::alternative_name::AlternativeName;
use dsp_domain::metadata::value::lang_text_data::LangTextData;
use sophia::ns::rdf::language;

use crate::api::convert::hcl::hcl_attribute::HclAttributes;
use crate::api::convert::hcl::hcl_block::HclBlock;
use crate::api::convert::hcl::value::lang_text_data;
use crate::error::DspMetaError;

const ALTERNATIVE_NAME_BLOCK_IDENTIFIER: &str = "alternative_name";
Expand All @@ -20,7 +23,11 @@ impl<'a> TryInto<AlternativeName> for HclBlock<'a> {
}

let attributes: Vec<&hcl::Attribute> = self.0.body.attributes().collect();
LangTextData::try_from(attributes).map(|lang_text_data| AlternativeName(lang_text_data.0))

// FIXME: improve API
let lang_text_data: Result<LangTextData, DspMetaError> =
HclAttributes(attributes).try_into();
lang_text_data.map(|l| l.into())
}
}

Expand Down
Loading

0 comments on commit a386725

Please sign in to comment.