diff --git a/crates/domain/src/id.rs b/crates/domain/src/id.rs index e408193..f7cc745 100644 --- a/crates/domain/src/id.rs +++ b/crates/domain/src/id.rs @@ -64,6 +64,10 @@ id_newtype!( /// Identifier for an authority record (person, organisation, or place). AuthorityId ); +id_newtype!( + /// Identifier for a catalogue object (or group of objects). + ObjectId +); #[cfg(test)] mod tests { diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index d5809c4..8c46507 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -4,10 +4,12 @@ mod audit; mod authority; mod id; mod label; +mod object; mod vocabulary; pub use audit::{AuditAction, AuditActor, AuditEntry, FieldChange, NewAuditEvent}; pub use authority::{Authority, AuthorityKind, AuthorityRef, NewAuthority}; -pub use id::{AuthorityId, OrgId, TermId, VocabularyId}; +pub use id::{AuthorityId, ObjectId, OrgId, TermId, VocabularyId}; pub use label::{LocalizedLabel, pick_label}; +pub use object::{CatalogueObject, ObjectInput, Visibility}; pub use vocabulary::{NewTerm, Term, TermRef, Vocabulary}; diff --git a/crates/domain/src/object.rs b/crates/domain/src/object.rs new file mode 100644 index 0000000..82fe50e --- /dev/null +++ b/crates/domain/src/object.rs @@ -0,0 +1,98 @@ +use serde::{Deserialize, Serialize}; +use time::{Date, OffsetDateTime}; + +use crate::ObjectId; + +/// Publication state of a catalogue record. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "lowercase")] +pub enum Visibility { + /// Work in progress; not shown anywhere public. + #[default] + Draft, + /// Complete but internal-only. + Internal, + /// Published; eligible for the public API. + Public, +} + +impl Visibility { + pub fn as_str(&self) -> &'static str { + match self { + Visibility::Draft => "draft", + Visibility::Internal => "internal", + Visibility::Public => "public", + } + } + + pub fn from_db(s: &str) -> Option { + match s { + "draft" => Some(Visibility::Draft), + "internal" => Some(Visibility::Internal), + "public" => Some(Visibility::Public), + _ => None, + } + } +} + +/// The mutable inventory-minimum fields of a catalogue object. +#[derive(Debug, Clone, PartialEq)] +pub struct ObjectInput { + pub object_number: String, + pub object_name: String, + pub number_of_objects: i32, + pub brief_description: Option, + pub current_location: Option, + pub current_owner: Option, + pub recorder: Option, + pub recording_date: Option, + pub visibility: Visibility, +} + +/// A catalogue object (or group of objects), read back from storage. +#[derive(Debug, Clone, PartialEq)] +pub struct CatalogueObject { + pub id: ObjectId, + pub object_number: String, + pub object_name: String, + pub number_of_objects: i32, + pub brief_description: Option, + pub current_location: Option, + pub current_owner: Option, + pub recorder: Option, + pub recording_date: Option, + pub visibility: Visibility, + pub created_at: OffsetDateTime, + pub updated_at: OffsetDateTime, +} + +impl CatalogueObject { + /// The mutable fields as an [`ObjectInput`] (used to diff against an update). + pub fn to_input(&self) -> ObjectInput { + ObjectInput { + object_number: self.object_number.clone(), + object_name: self.object_name.clone(), + number_of_objects: self.number_of_objects, + brief_description: self.brief_description.clone(), + current_location: self.current_location.clone(), + current_owner: self.current_owner.clone(), + recorder: self.recorder.clone(), + recording_date: self.recording_date, + visibility: self.visibility, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn visibility_round_trips_and_defaults_to_draft() { + for v in [Visibility::Draft, Visibility::Internal, Visibility::Public] { + assert_eq!(Visibility::from_db(v.as_str()), Some(v)); + } + assert_eq!(Visibility::from_db("secret"), None); + assert_eq!(Visibility::default(), Visibility::Draft); + } +}