cdx_core/extensions/semantic/
citation.rs1use serde::de::{self, MapAccess, Visitor};
4use serde::ser::SerializeMap;
5use serde::{Deserialize, Serialize};
6
7use crate::content::Block;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Citation {
19 pub refs: Vec<String>,
21
22 pub locator: Option<String>,
24
25 pub locator_type: Option<LocatorType>,
27
28 pub prefix: Option<String>,
30
31 pub suffix: Option<String>,
33
34 pub suppress_author: bool,
36}
37
38impl Citation {
39 #[must_use]
41 pub fn new(reference: impl Into<String>) -> Self {
42 Self {
43 refs: vec![reference.into()],
44 locator: None,
45 locator_type: None,
46 prefix: None,
47 suffix: None,
48 suppress_author: false,
49 }
50 }
51
52 #[must_use]
54 pub fn multi(refs: Vec<String>) -> Self {
55 Self {
56 refs,
57 locator: None,
58 locator_type: None,
59 prefix: None,
60 suffix: None,
61 suppress_author: false,
62 }
63 }
64
65 #[must_use]
67 pub fn first_ref(&self) -> Option<&str> {
68 self.refs.first().map(String::as_str)
69 }
70
71 #[must_use]
73 pub fn refs(&self) -> &[String] {
74 &self.refs
75 }
76
77 #[must_use]
79 pub fn with_page(mut self, page: impl Into<String>) -> Self {
80 self.locator = Some(page.into());
81 self.locator_type = Some(LocatorType::Page);
82 self
83 }
84
85 #[must_use]
87 pub fn with_locator(mut self, locator: impl Into<String>, locator_type: LocatorType) -> Self {
88 self.locator = Some(locator.into());
89 self.locator_type = Some(locator_type);
90 self
91 }
92
93 #[must_use]
95 pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
96 self.prefix = Some(prefix.into());
97 self
98 }
99
100 #[must_use]
102 pub fn with_suffix(mut self, suffix: impl Into<String>) -> Self {
103 self.suffix = Some(suffix.into());
104 self
105 }
106
107 #[must_use]
109 pub const fn suppress_author(mut self) -> Self {
110 self.suppress_author = true;
111 self
112 }
113}
114
115impl Serialize for Citation {
116 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
117 let mut count = 1; if self.locator.is_some() {
119 count += 1;
120 }
121 if self.locator_type.is_some() {
122 count += 1;
123 }
124 if self.prefix.is_some() {
125 count += 1;
126 }
127 if self.suffix.is_some() {
128 count += 1;
129 }
130 if self.suppress_author {
131 count += 1;
132 }
133
134 let mut map = serializer.serialize_map(Some(count))?;
135 map.serialize_entry("refs", &self.refs)?;
136 if let Some(ref locator) = self.locator {
137 map.serialize_entry("locator", locator)?;
138 }
139 if let Some(ref locator_type) = self.locator_type {
140 map.serialize_entry("locatorType", locator_type)?;
141 }
142 if let Some(ref prefix) = self.prefix {
143 map.serialize_entry("prefix", prefix)?;
144 }
145 if let Some(ref suffix) = self.suffix {
146 map.serialize_entry("suffix", suffix)?;
147 }
148 if self.suppress_author {
149 map.serialize_entry("suppressAuthor", &true)?;
150 }
151 map.end()
152 }
153}
154
155impl<'de> Deserialize<'de> for Citation {
156 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
157 struct CitationVisitor;
158
159 impl<'de> Visitor<'de> for CitationVisitor {
160 type Value = Citation;
161
162 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 formatter.write_str("a Citation object with 'refs' (array) or 'ref' (string)")
164 }
165
166 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Citation, A::Error> {
167 let mut refs: Option<Vec<String>> = None;
168 let mut locator: Option<String> = None;
169 let mut locator_type: Option<LocatorType> = None;
170 let mut prefix: Option<String> = None;
171 let mut suffix: Option<String> = None;
172 let mut suppress_author = false;
173
174 while let Some(key) = map.next_key::<String>()? {
175 match key.as_str() {
176 "refs" => {
177 refs = Some(map.next_value::<Vec<String>>()?);
179 }
180 "ref" => {
181 let single: String = map.next_value()?;
183 refs = Some(vec![single]);
184 }
185 "locator" => locator = Some(map.next_value()?),
186 "locatorType" | "locator_type" => locator_type = Some(map.next_value()?),
187 "prefix" => prefix = Some(map.next_value()?),
188 "suffix" => suffix = Some(map.next_value()?),
189 "suppressAuthor" | "suppress_author" => {
190 suppress_author = map.next_value()?;
191 }
192 _ => {
193 let _: serde_json::Value = map.next_value()?;
195 }
196 }
197 }
198
199 let refs = refs.ok_or_else(|| de::Error::missing_field("refs"))?;
200
201 Ok(Citation {
202 refs,
203 locator,
204 locator_type,
205 prefix,
206 suffix,
207 suppress_author,
208 })
209 }
210 }
211
212 deserializer.deserialize_map(CitationVisitor)
213 }
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
218#[serde(rename_all = "lowercase")]
219pub enum LocatorType {
220 Page,
222 Chapter,
224 Section,
226 Paragraph,
228 Verse,
230 Line,
232 Figure,
234 Table,
236 Equation,
238 Timestamp,
240}
241
242#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
247#[serde(rename_all = "camelCase")]
248pub struct Footnote {
249 pub number: u32,
251
252 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub id: Option<String>,
255
256 #[serde(default, skip_serializing_if = "Option::is_none")]
259 pub content: Option<String>,
260
261 #[serde(default, skip_serializing_if = "Vec::is_empty")]
264 pub children: Vec<Block>,
265}
266
267impl Footnote {
268 #[must_use]
270 pub fn new(number: u32) -> Self {
271 Self {
272 number,
273 id: None,
274 content: None,
275 children: Vec::new(),
276 }
277 }
278
279 #[must_use]
281 pub fn with_id(mut self, id: impl Into<String>) -> Self {
282 self.id = Some(id.into());
283 self
284 }
285
286 #[must_use]
291 pub fn with_content(mut self, content: impl Into<String>) -> Self {
292 self.content = Some(content.into());
293 self
294 }
295
296 #[must_use]
301 pub fn with_children(mut self, children: Vec<Block>) -> Self {
302 self.children = children;
303 self
304 }
305
306 #[must_use]
308 pub fn has_children(&self) -> bool {
309 !self.children.is_empty()
310 }
311
312 #[must_use]
314 pub fn has_content(&self) -> bool {
315 self.content.is_some()
316 }
317}