citeworks_cff/
names.rs

1//! Types and utilities for names e.g. of authors.
2
3use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
4use serde_yaml::{Mapping, Value};
5use url::Url;
6
7use crate::Date;
8
9/// Information about a person or entity.
10#[derive(Debug, Clone, Eq, PartialEq)]
11pub enum Name {
12	/// A human person.
13	Person(PersonName),
14
15	/// An entity, e.g. research institution, company, co-op...
16	Entity(EntityName),
17
18	/// A truly anonymous author.
19	///
20	/// This is the entry `- name: anonymous`.
21	Anonymous,
22}
23
24impl Name {
25	/// Returns true if the [Name] is a person.
26	pub fn is_person(&self) -> bool {
27		matches!(self, Self::Person(_))
28	}
29
30	/// Returns true if the [Name] is an entity.
31	pub fn is_entity(&self) -> bool {
32		matches!(self, Self::Entity(_))
33	}
34
35	/// Returns true if the [Name] is anonymous.
36	pub fn is_anonymous(&self) -> bool {
37		matches!(self, Self::Anonymous)
38	}
39
40	/// If the [Name] is a person, return it.
41	pub fn as_person(&self) -> Option<&PersonName> {
42		if let Self::Person(p) = self {
43			Some(p)
44		} else {
45			None
46		}
47	}
48
49	/// If the [Name] is an entity, return it.
50	pub fn as_entity(&self) -> Option<&EntityName> {
51		if let Self::Entity(e) = self {
52			Some(e)
53		} else {
54			None
55		}
56	}
57}
58
59impl Serialize for Name {
60	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
61	where
62		S: Serializer,
63	{
64		match self {
65			Self::Person(p) => p.serialize(serializer),
66			Self::Entity(e) => e.serialize(serializer),
67			Self::Anonymous => Mapping::from_iter([(
68				Value::String("name".into()),
69				Value::String("anonymous".into()),
70			)])
71			.serialize(serializer),
72		}
73	}
74}
75
76impl<'de> Deserialize<'de> for Name {
77	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
78	where
79		D: Deserializer<'de>,
80	{
81		let yaml = Mapping::deserialize(deserializer)?;
82		if let Some(name) = yaml.get("name") {
83			if let Value::String(name) = name {
84				if name == "anonymous" {
85					Ok(Name::Anonymous)
86				} else {
87					let entity: EntityName = serde_yaml::from_value(Value::Mapping(yaml))
88						.map_err(|e| D::Error::custom(e.to_string()))?;
89					Ok(Name::Entity(entity))
90				}
91			} else {
92				Err(D::Error::custom(
93					"'name' value must be a string".to_string(),
94				))
95			}
96		} else {
97			let person: PersonName = serde_yaml::from_value(Value::Mapping(yaml))
98				.map_err(|e| D::Error::custom(e.to_string()))?;
99			Ok(Name::Person(person))
100		}
101	}
102}
103
104/// The name of a person.
105///
106/// At least one field must be provided.
107#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
108#[serde(rename_all = "kebab-case")]
109pub struct PersonName {
110	/// Family names.
111	///
112	/// This includes combinations of given and patronymic forms, such as
113	/// _Guðmundsdóttir_ or _bin Osman_; double names with or without hyphen,
114	/// such as _Leutheusser-Schnarrenberger_ or _Sánchez Vicario_. It can
115	/// potentially also specify names that include prepositions or (nobiliary)
116	/// particles, especially if they occur in between family names such as in
117	/// Spanish- or Portuguese-origin names, such as _Fernández de Córdoba_.
118	#[serde(default, skip_serializing_if = "Option::is_none")]
119	pub family_names: Option<String>,
120
121	/// Given or chosen names.
122	#[serde(default, skip_serializing_if = "Option::is_none")]
123	pub given_names: Option<String>,
124
125	/// The person's name particle.
126	///
127	/// For example, a [nobiliary] particle, or a preposition meaning _of_ or
128	/// _from_ (for example _von_ in _Alexander von Humboldt_).
129	///
130	/// This may also be called the "non-dropping particle".
131	///
132	/// [nobiliary]: https://en.wikipedia.org/wiki/Nobiliary_particle
133	#[serde(default, skip_serializing_if = "Option::is_none")]
134	pub name_particle: Option<String>,
135
136	/// The person's name suffix.
137	///
138	/// For example, _Jr._ for _Sammy Davis Jr._ or _III_ for _Frank Edwin
139	/// Wright III_.
140	#[serde(default, skip_serializing_if = "Option::is_none")]
141	pub name_suffix: Option<String>,
142
143	/// Affiliation (e.g. organisation membership).
144	#[serde(default, skip_serializing_if = "Option::is_none")]
145	pub affiliation: Option<String>,
146
147	/// Common name metadata fields.
148	#[serde(flatten)]
149	pub meta: NameMeta,
150}
151
152/// An entity, e.g. research institution, company, co-op...
153///
154/// At least one field must be provided.
155#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
156#[serde(rename_all = "kebab-case")]
157pub struct EntityName {
158	/// The name of the entity.
159	#[serde(default, skip_serializing_if = "Option::is_none")]
160	pub name: Option<String>,
161
162	/// The entity's starting date.
163	///
164	/// For example, a conference.
165	#[serde(default, skip_serializing_if = "Option::is_none")]
166	pub date_start: Option<Date>,
167
168	/// The entity's ending date.
169	///
170	/// For example, a conference.
171	#[serde(default, skip_serializing_if = "Option::is_none")]
172	pub date_end: Option<Date>,
173
174	/// Common author metadata fields.
175	#[serde(flatten)]
176	pub meta: NameMeta,
177}
178
179/// Fields common to both types of names (persons and entities).
180#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
181#[serde(rename_all = "kebab-case")]
182pub struct NameMeta {
183	/// [ORCID] identifier.
184	///
185	/// [ORCID]: https://orcid.org
186	#[serde(default, skip_serializing_if = "Option::is_none")]
187	pub orcid: Option<Url>,
188
189	/// Physical or postal address.
190	#[serde(default, skip_serializing_if = "Option::is_none")]
191	pub address: Option<String>,
192
193	/// Alias or pseudonym.
194	#[serde(default, skip_serializing_if = "Option::is_none")]
195	pub alias: Option<String>,
196
197	/// City.
198	#[serde(default, skip_serializing_if = "Option::is_none")]
199	pub city: Option<String>,
200
201	/// Country.
202	#[serde(default, skip_serializing_if = "Option::is_none")]
203	pub country: Option<String>,
204
205	/// Email address.
206	#[serde(default, skip_serializing_if = "Option::is_none")]
207	pub email: Option<String>,
208
209	/// Post code.
210	#[serde(default, skip_serializing_if = "Option::is_none")]
211	pub post_code: Option<String>,
212
213	/// Region.
214	#[serde(default, skip_serializing_if = "Option::is_none")]
215	pub region: Option<String>,
216
217	/// Location.
218	#[serde(default, skip_serializing_if = "Option::is_none")]
219	pub location: Option<String>,
220
221	/// Telephone number.
222	#[serde(default, skip_serializing_if = "Option::is_none")]
223	pub tel: Option<String>,
224
225	/// Fax number.
226	#[serde(default, skip_serializing_if = "Option::is_none")]
227	pub fax: Option<String>,
228
229	/// Website.
230	#[serde(default, skip_serializing_if = "Option::is_none")]
231	pub website: Option<Url>,
232}