From eec9f89c58bc448f93e4660ce9fa70dd87fa1a18 Mon Sep 17 00:00:00 2001 From: Bruce D'Arcus Date: Sat, 17 Jun 2023 08:10:27 -0400 Subject: [PATCH] feat(bib): structured and multilingual titles Add a Title enum to offer different title input options. Signed-off-by: Bruce D'Arcus --- bibliography/src/reference.rs | 73 ++++++++++++++++++++++++++++++- processor/src/lib.rs | 10 ++--- processor/tests/processor_test.rs | 2 +- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/bibliography/src/reference.rs b/bibliography/src/reference.rs index 7dfc264..88115b9 100644 --- a/bibliography/src/reference.rs +++ b/bibliography/src/reference.rs @@ -1,3 +1,27 @@ +/* +SPDX-License-Identifier: MPL-2.0 +SPDX-FileCopyrightText: © 2023 Bruce D'Arcus +*/ + +//! A reference is a bibliographic item, such as a book, article, or web page. +//! It is the basic unit of bibliographic data. +//! +//! The model includes the following core data types. +//! Each is designed to be as simple as possible, while also allowing more complex data structures. +//! +//! ## Title +//! +//! A title can be a single string, a structured title, or a multilingual title. +//! +//! ## Contributor +//! +//! A contributor can be a single string, a structured name, or a list of contributors. +//! +//! ## Date +//! +//! Dates can either be EDTF strings, for flexible dates and date-times, or literal strings. +//! Literal strings can be used for examples like "Han Dynasty". + use edtf::level_1::Edtf; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -9,7 +33,7 @@ use url::Url; #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] pub struct InputReference { pub id: Option, - pub title: Option, + pub title: Option, pub author: Option<Contributor>, pub editor: Option<Contributor>, pub translator: Option<Contributor>, @@ -20,6 +44,8 @@ pub struct InputReference { pub note: Option<String>, } +pub type LangID = String; + #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] pub struct ContributorList(pub Vec<Contributor>); @@ -30,6 +56,51 @@ pub struct StructuredName { pub family_name: String, } +/// A collection of formattable strings consisting of a title, a translated +/// title, and a shorthand. +#[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] +#[serde(untagged)] +#[non_exhaustive] +// REVIEW this needs a bit more work. +pub enum Title { + /// A title in a single language. + Single(String), + /// A structured title. + Structured(StructuredTitle), + /// A title in multiple languages. + Multi(Vec<(LangID, String)>), + /// A structured title in multiple languages. + MultiStructured(Vec<(LangID, StructuredTitle)>), + /// An abbreviated title. + // Borrowed from Hayagriva + Shorthand(String, String), +} + +/// Where title parts are meaningful, use this struct; CSLN processors will not parse title strings. +#[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] +pub struct StructuredTitle { + pub full: String, + pub main: Option<String>, + pub sub: Option<Vec<String>>, +} + +impl fmt::Display for Title { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Title::Single(s) => write!(f, "{}", s), + Title::Multi(_m) => todo!("multilingual title"), + Title::Structured(s) => write!( + f, + "{}: {}", + s.main.clone().unwrap(), + s.sub.clone().unwrap().join(", ") + ), + Title::MultiStructured(_m) => todo!("multilingual structured title"), + Title::Shorthand(s, t) => write!(f, "{} ({})", s, t), + } + } +} + #[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)] pub struct EdtfString(pub String); diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 3c0a145..729e16b 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -197,11 +197,11 @@ impl RenderTitle for StyleTemplateTitle { _hints: &ProcHints, _options: &RenderOptions, ) -> String { - let title: &str = match &self.title { - Titles::Title => reference.title.as_ref().unwrap(), + let title: String = match &self.title { + Titles::Title => reference.title.as_ref().unwrap().to_string(), Titles::ContainerTitle => todo!(), }; - title.to_string() + title } } @@ -461,8 +461,8 @@ impl Processor { } SortGroupKey::Title => { references.par_sort_by(|a, b| { - let a_title = a.title.as_ref().unwrap().to_lowercase(); - let b_title = b.title.as_ref().unwrap().to_lowercase(); + let a_title = a.title.as_ref().unwrap().to_string().to_lowercase(); + let b_title = b.title.as_ref().unwrap().to_string().to_lowercase(); if sort.order == SortOrder::Ascending { a_title.cmp(&b_title) } else { diff --git a/processor/tests/processor_test.rs b/processor/tests/processor_test.rs index 47771a3..ed5e90e 100644 --- a/processor/tests/processor_test.rs +++ b/processor/tests/processor_test.rs @@ -13,7 +13,7 @@ mod tests { let refs = processor.get_references(); let sorted_refs = processor.sort_references(refs); assert_eq!(sorted_refs.len(), 36); - assert_eq!(sorted_refs.last().unwrap().title.as_deref(), Some("Title 4")); + assert_eq!(sorted_refs.last().unwrap().title.as_ref().unwrap().to_string(), "Title 4"); } #[test]