diff --git a/Cargo.toml b/Cargo.toml index 79f47f1b..f3194b43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,11 @@ tracing-test.workspace = true url.workspace = true [[bin]] -name = "dsp_meta" +name = "dsp_meta_cli" +test = false +doc = false + +[[bin]] +name = "dsp_meta_server" test = false doc = false diff --git a/src/bin/dsp_meta/cli.rs b/src/bin/dsp_meta_cli/cli.rs similarity index 100% rename from src/bin/dsp_meta/cli.rs rename to src/bin/dsp_meta_cli/cli.rs diff --git a/src/bin/dsp_meta/main.rs b/src/bin/dsp_meta_cli/main.rs similarity index 100% rename from src/bin/dsp_meta/main.rs rename to src/bin/dsp_meta_cli/main.rs diff --git a/src/bin/dsp_meta_server/main.rs b/src/bin/dsp_meta_server/main.rs new file mode 100644 index 00000000..e514d38b --- /dev/null +++ b/src/bin/dsp_meta_server/main.rs @@ -0,0 +1,17 @@ +use dsp_meta::errors::DspMetaError; +use tracing::{trace, Level}; +use tracing_subscriber::FmtSubscriber; + +fn main() -> Result<(), DspMetaError> { + // configure tracing library + let subscriber = FmtSubscriber::builder() + .with_max_level(Level::TRACE) + .finish(); + + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + trace!("Hello, world!"); + + dsp_meta::operation::server::run(); + Ok(()) +} diff --git a/src/dsp_meta/domain/entity/dataset.rs b/src/dsp_meta/domain/entity/dataset.rs index 09353a8b..f6343382 100644 --- a/src/dsp_meta/domain/entity/dataset.rs +++ b/src/dsp_meta/domain/entity/dataset.rs @@ -1,7 +1,7 @@ use crate::domain::value::Title; use crate::errors::DspMetaError; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct Dataset { pub title: Title, } diff --git a/src/dsp_meta/domain/entity/grant.rs b/src/dsp_meta/domain/entity/grant.rs index 83922de5..e8119bfc 100644 --- a/src/dsp_meta/domain/entity/grant.rs +++ b/src/dsp_meta/domain/entity/grant.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct Grant { id: String, } diff --git a/src/dsp_meta/domain/entity/mod.rs b/src/dsp_meta/domain/entity/mod.rs index b5ee66bf..04c2d7c0 100644 --- a/src/dsp_meta/domain/entity/mod.rs +++ b/src/dsp_meta/domain/entity/mod.rs @@ -1,6 +1,6 @@ pub(crate) mod dataset; pub(crate) mod grant; -pub(crate) mod metadata; mod organization; mod person; mod project; +pub mod project_metadata; diff --git a/src/dsp_meta/domain/entity/organization.rs b/src/dsp_meta/domain/entity/organization.rs index 6ba0158e..bbb77174 100644 --- a/src/dsp_meta/domain/entity/organization.rs +++ b/src/dsp_meta/domain/entity/organization.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct Organization { id: String, } diff --git a/src/dsp_meta/domain/entity/person.rs b/src/dsp_meta/domain/entity/person.rs index e6ab8d82..e33c4aea 100644 --- a/src/dsp_meta/domain/entity/person.rs +++ b/src/dsp_meta/domain/entity/person.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct Person { id: String, } diff --git a/src/dsp_meta/domain/entity/project.rs b/src/dsp_meta/domain/entity/project.rs index 0f5ffeb7..f4e45557 100644 --- a/src/dsp_meta/domain/entity/project.rs +++ b/src/dsp_meta/domain/entity/project.rs @@ -10,7 +10,7 @@ use crate::domain::value::{ }; use crate::errors::DspMetaError; -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Project { pub created_at: CreatedAt, pub created_by: CreatedBy, diff --git a/src/dsp_meta/domain/entity/metadata.rs b/src/dsp_meta/domain/entity/project_metadata.rs similarity index 93% rename from src/dsp_meta/domain/entity/metadata.rs rename to src/dsp_meta/domain/entity/project_metadata.rs index 07471fc1..34ceca0b 100644 --- a/src/dsp_meta/domain/entity/metadata.rs +++ b/src/dsp_meta/domain/entity/project_metadata.rs @@ -7,8 +7,8 @@ use crate::domain::value::version::Version; use crate::errors::DspMetaError; /// The Metadata struct represents the metadata of a DSP project. -#[derive(Debug, Default, PartialEq)] -pub struct Metadata { +#[derive(Debug, Default, Clone, PartialEq)] +pub struct ProjectMetadata { pub version: Version, pub project: Project, pub datasets: Vec, @@ -17,7 +17,7 @@ pub struct Metadata { pub persons: Vec, } -impl TryFrom<&hcl::Body> for Metadata { +impl TryFrom<&hcl::Body> for ProjectMetadata { type Error = DspMetaError; fn try_from(body: &hcl::Body) -> Result { @@ -54,7 +54,7 @@ impl TryFrom<&hcl::Body> for Metadata { } } - let metadata = Metadata { + let metadata = ProjectMetadata { version: version.ok_or_else(|| { DspMetaError::ParseVersion("Version attribute is not provided.".to_string()) })?, diff --git a/src/dsp_meta/domain/mod.rs b/src/dsp_meta/domain/mod.rs index 475b4b61..ab37a738 100644 --- a/src/dsp_meta/domain/mod.rs +++ b/src/dsp_meta/domain/mod.rs @@ -1,12 +1,12 @@ mod convert; -pub(crate) mod entity; +pub mod entity; pub mod value; #[cfg(test)] mod tests { use hcl::body; - use crate::domain::entity::metadata::Metadata; + use crate::domain::entity::project_metadata::ProjectMetadata; #[test] fn try_from_multiple_projects_error() { @@ -19,7 +19,7 @@ mod tests { } ); - let project = Metadata::try_from(&input); + let project = ProjectMetadata::try_from(&input); assert!(project.is_err()); } @@ -27,7 +27,7 @@ mod tests { fn try_from_no_project_error() { let input = body!(); - let project = Metadata::try_from(&input); + let project = ProjectMetadata::try_from(&input); assert!(project.is_err()); } } diff --git a/src/dsp_meta/domain/value/discipline.rs b/src/dsp_meta/domain/value/discipline.rs index 9d54bb88..0c11141f 100644 --- a/src/dsp_meta/domain/value/discipline.rs +++ b/src/dsp_meta/domain/value/discipline.rs @@ -14,7 +14,7 @@ use crate::errors::DspMetaError; /// url = "https://skos.um.es/unesco6/5501" /// } /// ``` -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Discipline { Skos(RefData), Snf(RefData), diff --git a/src/dsp_meta/domain/value/keyword.rs b/src/dsp_meta/domain/value/keyword.rs index 190260e7..8adb007e 100644 --- a/src/dsp_meta/domain/value/keyword.rs +++ b/src/dsp_meta/domain/value/keyword.rs @@ -6,7 +6,7 @@ use crate::errors::DspMetaError; const KEYWORD_BLOCK_IDENTIFIER: &str = "keyword"; -#[derive(Debug, Default, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct Keyword(HashMap); impl TryFrom<&hcl::Block> for Keyword { diff --git a/src/dsp_meta/domain/value/lang_text_data.rs b/src/dsp_meta/domain/value/lang_text_data.rs index 71e6e826..886182ff 100644 --- a/src/dsp_meta/domain/value/lang_text_data.rs +++ b/src/dsp_meta/domain/value/lang_text_data.rs @@ -4,7 +4,7 @@ use crate::domain::value::iso_code::IsoCode; use crate::errors::DspMetaError; /// Represents multiple strings in different languages. -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct LangTextData(pub HashMap); /// FIXME: Move to the API layer where the service adapter will be implemented diff --git a/src/dsp_meta/domain/value/mod.rs b/src/dsp_meta/domain/value/mod.rs index 51863c5b..cf4a8219 100644 --- a/src/dsp_meta/domain/value/mod.rs +++ b/src/dsp_meta/domain/value/mod.rs @@ -39,8 +39,8 @@ pub struct StartDate(pub String); #[derive(Debug, Default, Clone, PartialEq)] pub struct EndDate(pub String); -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct ContactPoint(pub String); -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Title(pub String); diff --git a/src/dsp_meta/domain/value/publication.rs b/src/dsp_meta/domain/value/publication.rs index f9f72ca6..b7116b39 100644 --- a/src/dsp_meta/domain/value/publication.rs +++ b/src/dsp_meta/domain/value/publication.rs @@ -3,7 +3,7 @@ use crate::errors::DspMetaError; const PUBLICATION_BLOCK_IDENTIFIER: &str = "publication"; -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Publication { SimpleText(SimpleTextData), } diff --git a/src/dsp_meta/domain/value/ref_data.rs b/src/dsp_meta/domain/value/ref_data.rs index c65578c7..924f65a3 100644 --- a/src/dsp_meta/domain/value/ref_data.rs +++ b/src/dsp_meta/domain/value/ref_data.rs @@ -2,7 +2,7 @@ use tracing::warn; use crate::errors::DspMetaError; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct RefData { pub(crate) ref_id: String, pub(crate) description: String, diff --git a/src/dsp_meta/domain/value/simple_text_data.rs b/src/dsp_meta/domain/value/simple_text_data.rs index 9ef83154..bb125844 100644 --- a/src/dsp_meta/domain/value/simple_text_data.rs +++ b/src/dsp_meta/domain/value/simple_text_data.rs @@ -4,7 +4,7 @@ use crate::errors::DspMetaError; const TEXT_ATTRIBUTE_IDENTIFIER: &str = "text"; -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct SimpleTextData(pub String); impl TryFrom> for SimpleTextData { diff --git a/src/dsp_meta/domain/value/url.rs b/src/dsp_meta/domain/value/url.rs index 3354c205..747bb4b2 100644 --- a/src/dsp_meta/domain/value/url.rs +++ b/src/dsp_meta/domain/value/url.rs @@ -19,7 +19,7 @@ const LABEL_ATTRIBUTE_KEY: &str = "label"; /// /// Use [`Attribute::new`] to construct an [`Attribute`] from a value that is convertible to this /// crate's [`Expression`] type. -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Url { pub href: url::Url, pub label: String, diff --git a/src/dsp_meta/domain/value/version.rs b/src/dsp_meta/domain/value/version.rs index b00a2194..b5fd41b5 100644 --- a/src/dsp_meta/domain/value/version.rs +++ b/src/dsp_meta/domain/value/version.rs @@ -1,6 +1,6 @@ use hcl::Expression; -#[derive(Debug, Default, PartialEq)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct Version(pub u64); /// Given a list of attributes, try to extract the version. diff --git a/src/dsp_meta/errors.rs b/src/dsp_meta/errors.rs index a4bb500b..5f1facba 100644 --- a/src/dsp_meta/errors.rs +++ b/src/dsp_meta/errors.rs @@ -9,6 +9,7 @@ pub enum DspMetaError { ParseProject(String), ParseDataset(String), CreateValueObject(String), + NotFound, } impl From for DspMetaError { diff --git a/src/dsp_meta/lib.rs b/src/dsp_meta/lib.rs index fa0ec7de..54797b0f 100644 --- a/src/dsp_meta/lib.rs +++ b/src/dsp_meta/lib.rs @@ -1,19 +1,8 @@ pub mod domain; pub mod errors; pub mod operation; - -use std::path::Path; - -use domain::entity::metadata::Metadata; - -use crate::errors::DspMetaError; - -pub fn load>(path: P) -> Result { - let input = std::fs::read_to_string(path)?; - let body: hcl::Body = hcl::from_str(&input)?; - let metadata = Metadata::try_from(&body)?; - Ok(metadata) -} +mod repo; +mod service; #[cfg(test)] mod tests { diff --git a/src/dsp_meta/operation/convert.rs b/src/dsp_meta/operation/convert.rs index 691eabcb..79f13435 100644 --- a/src/dsp_meta/operation/convert.rs +++ b/src/dsp_meta/operation/convert.rs @@ -2,12 +2,14 @@ use std::path::Path; use tracing::info; +use crate::domain::entity::project_metadata::ProjectMetadata; use crate::errors::DspMetaError; -use crate::load; pub fn convert>(source_path: &P, _target_path: &P) -> Result<(), DspMetaError> { info!("Hello from convert!"); - let _ = load(source_path)?; + let input = std::fs::read_to_string(source_path)?; + let body: hcl::Body = hcl::from_str(&input)?; + let _metadata = ProjectMetadata::try_from(&body)?; Ok(()) } diff --git a/src/dsp_meta/operation/mod.rs b/src/dsp_meta/operation/mod.rs index afde9c84..598f780e 100644 --- a/src/dsp_meta/operation/mod.rs +++ b/src/dsp_meta/operation/mod.rs @@ -1,2 +1,3 @@ pub mod convert; +pub mod server; pub mod validate; diff --git a/src/dsp_meta/operation/server.rs b/src/dsp_meta/operation/server.rs new file mode 100644 index 00000000..5ceea1c7 --- /dev/null +++ b/src/dsp_meta/operation/server.rs @@ -0,0 +1,9 @@ +use tracing::trace; + +use crate::repo::project_metadata_repository::ProjectMetadataRepository; +use crate::service::project_metadata_service::ProjectMetadataService; + +pub fn run() { + trace!("tracing from dsp_meta::core::server::run()"); + let _project_metadata_service = ProjectMetadataService::new(ProjectMetadataRepository::new()); +} diff --git a/src/dsp_meta/operation/validate.rs b/src/dsp_meta/operation/validate.rs index 1a441ab4..167ee626 100644 --- a/src/dsp_meta/operation/validate.rs +++ b/src/dsp_meta/operation/validate.rs @@ -2,13 +2,15 @@ use std::path::Path; use tracing::info; +use crate::domain::entity::project_metadata::ProjectMetadata; use crate::errors::DspMetaError; -use crate::load; /// Read projects definition from .toml pub fn validate>(project_path: &P) -> Result<(), DspMetaError> { info!("Hello from validate!"); - let _ = load(project_path)?; + let input = std::fs::read_to_string(project_path)?; + let body: hcl::Body = hcl::from_str(&input)?; + let _metadata = ProjectMetadata::try_from(&body)?; Ok(()) } diff --git a/src/dsp_meta/repo/mod.rs b/src/dsp_meta/repo/mod.rs new file mode 100644 index 00000000..8343afed --- /dev/null +++ b/src/dsp_meta/repo/mod.rs @@ -0,0 +1 @@ +pub(crate) mod project_metadata_repository; diff --git a/src/dsp_meta/repo/project_metadata_repository.rs b/src/dsp_meta/repo/project_metadata_repository.rs new file mode 100644 index 00000000..0d04909c --- /dev/null +++ b/src/dsp_meta/repo/project_metadata_repository.rs @@ -0,0 +1,73 @@ +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +use tracing::trace; + +use crate::domain::entity::project_metadata::ProjectMetadata; +use crate::domain::value::Shortcode; +use crate::errors::DspMetaError; +use crate::service::project_metadata_repository_contract::ProjectMetadataRepositoryContract; + +pub struct ProjectMetadataRepository { + db: Arc>>, +} + +impl ProjectMetadataRepository { + pub fn new() -> Self { + trace!("ProjectMetadataRepository::new"); + let db: Arc>> = + Arc::new(RwLock::new(HashMap::new())); + Self { db } + } +} + +impl ProjectMetadataRepositoryContract for ProjectMetadataRepository { + fn get_by_shortcode(&self, shortcode: Shortcode) -> Result { + let db = self.db.read().unwrap(); + match db.get(shortcode.0.as_str()) { + Some(metadata) => Ok(metadata.clone()), + None => Err(DspMetaError::NotFound), + } + } + + fn get_all(&self) -> Result, DspMetaError> { + let mut result: Vec = vec![]; + let db = self.db.read().unwrap(); + + for project_metadata in db.values() { + result.push(project_metadata.clone()); + } + + Ok(result) + } + + fn store(&self, shortcode: Shortcode, metadata: ProjectMetadata) -> Result<(), DspMetaError> { + let mut db = self.db.write().unwrap(); + db.insert(shortcode.0, metadata); + Ok(()) + } + + fn delete(&self, shortcode: Shortcode) -> Result<(), DspMetaError> { + let mut db = self.db.write().unwrap(); + + match db.remove(shortcode.0.as_str()) { + Some(_) => Ok(()), + None => Ok(()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn successfully_store_project_metadata() { + let metadata = ProjectMetadata::default(); + let shortcode = Shortcode("1234".to_owned()); + + let repo = ProjectMetadataRepository::new(); + let result = repo.store(shortcode, metadata); + assert!(result.is_ok()); + } +} diff --git a/src/dsp_meta/service/mod.rs b/src/dsp_meta/service/mod.rs new file mode 100644 index 00000000..2e3abfdd --- /dev/null +++ b/src/dsp_meta/service/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod project_metadata_api_contract; +pub(crate) mod project_metadata_repository_contract; +pub(crate) mod project_metadata_service; diff --git a/src/dsp_meta/service/project_metadata_api_contract.rs b/src/dsp_meta/service/project_metadata_api_contract.rs new file mode 100644 index 00000000..1ea69c7a --- /dev/null +++ b/src/dsp_meta/service/project_metadata_api_contract.rs @@ -0,0 +1,10 @@ +use crate::domain::entity::project_metadata::ProjectMetadata; +use crate::domain::value::Shortcode; +use crate::errors::DspMetaError; + +pub trait ProjectMetadataApiContract { + fn get_by_shortcode(&self, id: Shortcode) -> Result; + fn get_all(&self) -> Result, DspMetaError>; + fn store(&self, id: Shortcode, metadata: ProjectMetadata) -> Result<(), DspMetaError>; + fn delete(&self, id: Shortcode) -> Result<(), DspMetaError>; +} diff --git a/src/dsp_meta/service/project_metadata_repository_contract.rs b/src/dsp_meta/service/project_metadata_repository_contract.rs new file mode 100644 index 00000000..b2c56fc0 --- /dev/null +++ b/src/dsp_meta/service/project_metadata_repository_contract.rs @@ -0,0 +1,10 @@ +use crate::domain::entity::project_metadata::ProjectMetadata; +use crate::domain::value::Shortcode; +use crate::errors::DspMetaError; + +pub trait ProjectMetadataRepositoryContract { + fn get_by_shortcode(&self, shortcode: Shortcode) -> Result; + fn get_all(&self) -> Result, DspMetaError>; + fn store(&self, shortcode: Shortcode, metadata: ProjectMetadata) -> Result<(), DspMetaError>; + fn delete(&self, shortcode: Shortcode) -> Result<(), DspMetaError>; +} diff --git a/src/dsp_meta/service/project_metadata_service.rs b/src/dsp_meta/service/project_metadata_service.rs new file mode 100644 index 00000000..05404766 --- /dev/null +++ b/src/dsp_meta/service/project_metadata_service.rs @@ -0,0 +1,42 @@ +use tracing::trace; + +use crate::domain::entity::project_metadata::ProjectMetadata; +use crate::domain::value::Shortcode; +use crate::errors::DspMetaError; +use crate::service::project_metadata_api_contract::ProjectMetadataApiContract; +use crate::service::project_metadata_repository_contract::ProjectMetadataRepositoryContract; + +pub struct ProjectMetadataService { + repo: R, +} + +impl ProjectMetadataService +where + R: ProjectMetadataRepositoryContract, +{ + pub fn new(repo: R) -> Self { + trace!("tracing from dsp_meta::core::project_metadata_service::new()"); + Self { repo } + } +} + +impl ProjectMetadataApiContract for ProjectMetadataService +where + R: ProjectMetadataRepositoryContract, +{ + fn get_by_shortcode(&self, id: Shortcode) -> Result { + self.repo.get_by_shortcode(id) + } + + fn get_all(&self) -> Result, DspMetaError> { + todo!() + } + + fn store(&self, id: Shortcode, metadata: ProjectMetadata) -> Result<(), DspMetaError> { + self.repo.store(id, metadata) + } + + fn delete(&self, id: Shortcode) -> Result<(), DspMetaError> { + self.repo.delete(id) + } +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 8cfb1244..81f05271 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,6 +1,11 @@ -#![allow(dead_code)] +use std::path::Path; -#[track_caller] -pub(crate) fn init() { - let _ = env_logger::builder().is_test(true).try_init(); +use dsp_meta::domain::entity::project_metadata::ProjectMetadata; +use dsp_meta::errors::DspMetaError; + +pub fn load>(path: P) -> Result { + let input = std::fs::read_to_string(path)?; + let body: hcl::Body = hcl::from_str(&input)?; + let metadata = ProjectMetadata::try_from(&body)?; + Ok(metadata) } diff --git a/tests/integration_test.rs b/tests/integration_test.rs index ad649380..babb62ff 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,4 +1,6 @@ -use dsp_meta::load; +mod common; + +use common::load; #[test] fn load_dokubib_config() {