From aa53ac307b3dbac8748d862087f5e238393b83d5 Mon Sep 17 00:00:00 2001 From: Daniel Drodt Date: Wed, 2 Oct 2024 12:57:10 +0200 Subject: [PATCH] Change publisher to dictionary (#105) --- docs/file-format.md | 11 +++++----- src/csl/taxonomy.rs | 15 ++++++++----- src/interop.rs | 16 ++++++++------ src/lib.rs | 6 ++--- src/types/mod.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/docs/file-format.md b/docs/file-format.md index 34899221..b9ef7c22 100644 --- a/docs/file-format.md +++ b/docs/file-format.md @@ -117,8 +117,9 @@ Parents can also appear as standalone items and can have parents themselves. Thi plaque: type: Misc title: Informational plaque about Jacoby's 1967 photos - publisher: Stiftung Reinbeckhallen - location: Berlin, Germany + publisher: + name: Stiftung Reinbeckhallen + location: Berlin, Germany date: 2020 parent: type: Artwork @@ -231,16 +232,16 @@ This section lists all possible fields and data types for them. | | | |------------------|-----------------------------------------------------------| -| **Data type:** | formattable string | +| **Data type:** | publisher | | **Description:** | publisher of the item | -| **Example:** | `publisher: Penguin Books` | +| **Example:** | `publisher: Penguin Books` or `publisher:
name: Penguin Books
location: London` | #### `location` | | | |------------------|-----------------------------------------------------------| | **Data type:** | formattable string | -| **Description:** | location at which the item was published or created | +| **Description:** | location at which an entry is physically located or took place. For the location where an item was published, see `publisher`. | | **Example:** | `location: Lahore, Pakistan` | #### `organization` diff --git a/src/csl/taxonomy.rs b/src/csl/taxonomy.rs index ebaa8b39..57483c00 100644 --- a/src/csl/taxonomy.rs +++ b/src/csl/taxonomy.rs @@ -3,7 +3,8 @@ use std::cmp; use std::str::FromStr; use crate::types::{ - ChunkedString, Date, EntryType, MaybeTyped, Numeric, Person, PersonRole, StringChunk, + ChunkedString, Date, EntryType, MaybeTyped, Numeric, Person, PersonRole, Publisher, + StringChunk, }; use crate::{Entry, PageRanges}; use citationberg::taxonomy::{ @@ -309,11 +310,12 @@ impl EntryLike for Entry { StandardVariable::OriginalPublisher => entry .get_original() .and_then(|e| e.publisher()) - .map(|f| f.select(form)) + .and_then(Publisher::name) + .map(|n| n.select(form)) .map(Cow::Borrowed), StandardVariable::OriginalPublisherPlace => entry .get_original() - .and_then(|e| e.publisher().and_then(|_| e.location())) + .and_then(|e| e.publisher().and_then(|p| p.location())) .map(|f| f.select(form)) .map(Cow::Borrowed), StandardVariable::OriginalTitle => entry @@ -330,11 +332,12 @@ impl EntryLike for Entry { } StandardVariable::Publisher => entry .map(|e| e.publisher()) - .map(|f| f.select(form)) + .and_then(Publisher::name) + .map(|n| n.select(form)) .map(Cow::Borrowed), StandardVariable::PublisherPlace => entry - .map(|e| if e.publisher().is_some() { Some(e) } else { None }) - .and_then(|e| e.location()) + .map(|e| e.publisher()) + .and_then(|p| p.location()) .map(|f| f.select(form)) .map(Cow::Borrowed), StandardVariable::References => None, diff --git a/src/interop.rs b/src/interop.rs index 20eb5039..02a3c027 100644 --- a/src/interop.rs +++ b/src/interop.rs @@ -452,16 +452,18 @@ impl TryFrom<&tex::Entry> for Entry { item.set_url(QualifiedUrl { value: url, visit_date: date }); } - if let Some(location) = map_res(entry.location())?.map(|d| d.into()) { + if let Some(publisher_name) = + map_res(entry.publisher())?.map(|pubs| comma_list(&pubs)) + { + let location = map_res(entry.location())?.map(|d| d.into()); + let publisher = Publisher::new(Some(publisher_name), location); if let Some(parent) = book(&mut item, parent) { - parent.set_location(location); + parent.set_publisher(publisher); } else { - item.set_location(location); + item.set_publisher(publisher); } - } - - if let Some(publisher) = map_res(entry.publisher())?.map(|pubs| comma_list(&pubs)) - { + } else if let Some(location) = map_res(entry.location())?.map(|d| d.into()) { + let publisher = Publisher::new(None, Some(location)); if let Some(parent) = book(&mut item, parent) { parent.set_publisher(publisher); } else { diff --git a/src/lib.rs b/src/lib.rs index 27cf999d..cf8d4818 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -505,9 +505,9 @@ entry! { #[serde(serialize_with = "serialize_one_or_many_opt")] #[serde(deserialize_with = "deserialize_one_or_many_opt")] "affiliated" => affiliated: Vec | [PersonsWithRoles], - /// Publisher of the item. - "publisher" => publisher: FormatString, - /// Physical location at which the item was published or created. + /// Publisher of the item, which may have a name and a location. + "publisher" => publisher: Publisher, + /// Physical location at which an entry is physically located or took place. "location" => location: FormatString, /// Organization at/for which the item was created. "organization" => organization: FormatString, diff --git a/src/types/mod.rs b/src/types/mod.rs index 02c6cb57..ebabfd69 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -411,6 +411,59 @@ impl Display for QualifiedUrl { } } +derive_or_from_str! { + /// A publisher, possibly with a location. + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + pub struct Publisher where "FormatString string or dictionary with \"name\" and \"location\"" { + /// Publisher of the item. + name: Option, + /// Physical location at which the item was published or created. + location: Option, + } +} + +impl Serialize for Publisher { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if let Some(location) = &self.location { + let mut map = serializer.serialize_map(Some(2))?; + map.serialize_entry("name", &self.name)?; + map.serialize_entry("location", location)?; + map.end() + } else { + self.name.serialize(serializer) + } + } +} + +impl Publisher { + /// Create a new publisher. + pub fn new(name: Option, location: Option) -> Self { + Self { name, location } + } + + /// Publisher of the item. + pub fn name(&self) -> Option<&FormatString> { + self.name.as_ref() + } + + /// Physical location at which the item was published or created. + pub fn location(&self) -> Option<&FormatString> { + self.location.as_ref() + } +} + +impl FromStr for Publisher { + type Err = ChunkedStrParseError; + + /// Creates a new publisher with `s` as its name and no location. + fn from_str(s: &str) -> Result { + Ok(Publisher::new(Some(FormatString::from_str(s)?), None)) + } +} + /// A set of serial numbers like DOIs, ISBNs, or ISSNs. /// Keys should be lowercase. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Hash)]