use serde::de::{self, MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize};
use crate::content::Block;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Citation {
pub refs: Vec<String>,
pub locator: Option<String>,
pub locator_type: Option<LocatorType>,
pub prefix: Option<String>,
pub suffix: Option<String>,
pub suppress_author: bool,
}
impl Citation {
#[must_use]
pub fn new(reference: impl Into<String>) -> Self {
Self {
refs: vec![reference.into()],
locator: None,
locator_type: None,
prefix: None,
suffix: None,
suppress_author: false,
}
}
#[must_use]
pub fn multi(refs: Vec<String>) -> Self {
Self {
refs,
locator: None,
locator_type: None,
prefix: None,
suffix: None,
suppress_author: false,
}
}
#[must_use]
pub fn first_ref(&self) -> Option<&str> {
self.refs.first().map(String::as_str)
}
#[must_use]
pub fn refs(&self) -> &[String] {
&self.refs
}
#[must_use]
pub fn with_page(mut self, page: impl Into<String>) -> Self {
self.locator = Some(page.into());
self.locator_type = Some(LocatorType::Page);
self
}
#[must_use]
pub fn with_locator(mut self, locator: impl Into<String>, locator_type: LocatorType) -> Self {
self.locator = Some(locator.into());
self.locator_type = Some(locator_type);
self
}
#[must_use]
pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
self.prefix = Some(prefix.into());
self
}
#[must_use]
pub fn with_suffix(mut self, suffix: impl Into<String>) -> Self {
self.suffix = Some(suffix.into());
self
}
#[must_use]
pub const fn suppress_author(mut self) -> Self {
self.suppress_author = true;
self
}
}
impl Serialize for Citation {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut count = 1; if self.locator.is_some() {
count += 1;
}
if self.locator_type.is_some() {
count += 1;
}
if self.prefix.is_some() {
count += 1;
}
if self.suffix.is_some() {
count += 1;
}
if self.suppress_author {
count += 1;
}
let mut map = serializer.serialize_map(Some(count))?;
map.serialize_entry("refs", &self.refs)?;
if let Some(ref locator) = self.locator {
map.serialize_entry("locator", locator)?;
}
if let Some(ref locator_type) = self.locator_type {
map.serialize_entry("locatorType", locator_type)?;
}
if let Some(ref prefix) = self.prefix {
map.serialize_entry("prefix", prefix)?;
}
if let Some(ref suffix) = self.suffix {
map.serialize_entry("suffix", suffix)?;
}
if self.suppress_author {
map.serialize_entry("suppressAuthor", &true)?;
}
map.end()
}
}
impl<'de> Deserialize<'de> for Citation {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct CitationVisitor;
impl<'de> Visitor<'de> for CitationVisitor {
type Value = Citation;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a Citation object with 'refs' (array) or 'ref' (string)")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Citation, A::Error> {
let mut refs: Option<Vec<String>> = None;
let mut locator: Option<String> = None;
let mut locator_type: Option<LocatorType> = None;
let mut prefix: Option<String> = None;
let mut suffix: Option<String> = None;
let mut suppress_author = false;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"refs" => {
refs = Some(map.next_value::<Vec<String>>()?);
}
"ref" => {
let single: String = map.next_value()?;
refs = Some(vec![single]);
}
"locator" => locator = Some(map.next_value()?),
"locatorType" | "locator_type" => locator_type = Some(map.next_value()?),
"prefix" => prefix = Some(map.next_value()?),
"suffix" => suffix = Some(map.next_value()?),
"suppressAuthor" | "suppress_author" => {
suppress_author = map.next_value()?;
}
_ => {
let _: serde_json::Value = map.next_value()?;
}
}
}
let refs = refs.ok_or_else(|| de::Error::missing_field("refs"))?;
Ok(Citation {
refs,
locator,
locator_type,
prefix,
suffix,
suppress_author,
})
}
}
deserializer.deserialize_map(CitationVisitor)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LocatorType {
Page,
Chapter,
Section,
Paragraph,
Verse,
Line,
Figure,
Table,
Equation,
Timestamp,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Footnote {
pub number: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub children: Vec<Block>,
}
impl Footnote {
#[must_use]
pub fn new(number: u32) -> Self {
Self {
number,
id: None,
content: None,
children: Vec::new(),
}
}
#[must_use]
pub fn with_id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
#[must_use]
pub fn with_content(mut self, content: impl Into<String>) -> Self {
self.content = Some(content.into());
self
}
#[must_use]
pub fn with_children(mut self, children: Vec<Block>) -> Self {
self.children = children;
self
}
#[must_use]
pub fn has_children(&self) -> bool {
!self.children.is_empty()
}
#[must_use]
pub fn has_content(&self) -> bool {
self.content.is_some()
}
}