use crate::error;
use crate::model::block::{BlockContent, HasBlockContent, Paragraph};
use crate::model::HasInnerContent;
#[cfg(feature = "fmt_json")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "fmt_json", derive(Serialize, Deserialize))]
pub enum Metadata {
Author(Author),
Copyright(Copyright),
Date(String),
Keywords(Vec<String>),
Revision(String),
Status(String),
Title(String),
Other(SimpleProperty),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "fmt_json", derive(Serialize, Deserialize))]
pub struct Author {
pub name: String,
#[cfg_attr(feature = "fmt_json", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "fmt_json", serde(default))]
pub email: Option<String>,
#[cfg_attr(feature = "fmt_json", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "fmt_json", serde(default))]
pub organization: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "fmt_json", derive(Serialize, Deserialize))]
pub struct Copyright {
pub year: u16,
#[cfg_attr(feature = "fmt_json", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "fmt_json", serde(default))]
pub organization: Option<String>,
#[cfg_attr(feature = "fmt_json", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "fmt_json", serde(default))]
pub comment: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "fmt_json", derive(Serialize, Deserialize))]
pub struct SimpleProperty {
pub key: String,
pub value: String,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "fmt_json", derive(Serialize, Deserialize))]
pub struct Document {
version: String,
#[cfg_attr(feature = "fmt_json", serde(skip_serializing_if = "Vec::is_empty"))]
#[cfg_attr(feature = "fmt_json", serde(default))]
metadata: Vec<Metadata>,
#[cfg_attr(feature = "fmt_json", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "fmt_json", serde(default))]
abstract_block: Option<Paragraph>,
content: Vec<BlockContent>,
}
impl Default for Document {
fn default() -> Self {
Self {
version: env!("CARGO_PKG_VERSION").to_string(),
metadata: Default::default(),
abstract_block: None,
content: Default::default(),
}
}
}
impl Into<Document> for BlockContent {
fn into(self) -> Document {
let mut doc = Document::default();
let _ = doc.add_content(self);
doc
}
}
has_block_impls!(Document);
impl Document {
pub fn has_metadata(&self) -> bool {
!self.metadata.is_empty()
}
pub fn metadata(&self) -> &Vec<Metadata> {
&self.metadata
}
pub fn add_metadata(&mut self, datum: Metadata) -> &mut Self {
self.metadata.push(datum);
self
}
pub fn set_title(&mut self, v: &str) -> &mut Self {
self.add_metadata(Metadata::Title(v.to_string()))
}
pub fn set_date(&mut self, v: &str) -> &mut Self {
self.add_metadata(Metadata::Date(v.to_string()))
}
pub fn add_author(&mut self, v: Author) -> &mut Self {
self.add_metadata(Metadata::Author(v))
}
pub fn add_author_str(
&mut self,
name: &str,
email: Option<&str>,
organization: Option<&str>,
) -> &mut Self {
self.add_metadata(Metadata::Author(Author {
name: name.to_string(),
email: email.map(str::to_string),
organization: organization.map(str::to_string),
}))
}
pub fn add_copyright(&mut self, v: Copyright) -> &mut Self {
self.add_metadata(Metadata::Copyright(v))
}
pub fn add_copyright_str(
&mut self,
year: u16,
organization: Option<&str>,
comment: Option<&str>,
) -> &mut Self {
self.add_metadata(Metadata::Copyright(Copyright {
year,
organization: organization.map(str::to_string),
comment: comment.map(str::to_string),
}))
}
pub fn add_keywords(&mut self, v: &[String]) -> &mut Self {
self.add_metadata(Metadata::Keywords(v.to_vec()))
}
pub fn add_keywords_str(&mut self, v: &[&str]) -> &mut Self {
self.add_metadata(Metadata::Keywords(
v.iter().cloned().map(str::to_string).collect(),
))
}
pub fn add_metadata_property(&mut self, v: SimpleProperty) -> &mut Self {
self.add_metadata(Metadata::Other(v))
}
pub fn add_metadata_property_str(&mut self, name: &str, value: &str) -> &mut Self {
self.add_metadata(Metadata::Other(SimpleProperty {
key: name.to_string(),
value: value.to_string(),
}))
}
pub fn has_abstract(&self) -> bool {
self.abstract_block.is_some()
}
pub fn add_abstract(&mut self, abstract_block: Paragraph) -> &mut Self {
self.abstract_block = Some(abstract_block);
self
}
pub fn abstract_block(&self) -> &Option<Paragraph> {
&self.abstract_block
}
}
impl Metadata {
pub fn key(&self) -> String {
match self {
Metadata::Author(_) => "author",
Metadata::Copyright(_) => "copyright",
Metadata::Date(_) => "date",
Metadata::Keywords(_) => "keywords",
Metadata::Revision(_) => "revision",
Metadata::Status(_) => "status",
Metadata::Title(_) => "title",
Metadata::Other(p) => &p.key,
}
.to_string()
}
pub fn value_string(&self) -> String {
match self {
Metadata::Author(value) => format!(
"{}{}{}",
value.name,
value
.email
.as_ref()
.map(|s| format!("({})", s))
.unwrap_or_default(),
value
.organization
.as_ref()
.map(|s| format!(" - {}", s))
.unwrap_or_default()
),
Metadata::Copyright(value) => format!(
"{}{}{}",
value.year,
value
.organization
.as_ref()
.map(|s| format!(" {}.", s))
.unwrap_or_default(),
value
.comment
.as_ref()
.map(|s| format!(" - {}.", s))
.unwrap_or_default()
),
Metadata::Date(value) => value.to_string(),
Metadata::Keywords(value) => format!("[{}]", value.join(", ")),
Metadata::Revision(value) => value.to_string(),
Metadata::Status(value) => value.to_string(),
Metadata::Title(value) => value.to_string(),
Metadata::Other(value) => value.value.to_string(),
}
}
pub fn yaml_string(&self) -> String {
match self {
Metadata::Author(value) => format!(
"author:\n- name: {}{}{}",
value.name,
value
.email
.as_ref()
.map(|s| format!("- email: {}\n", s))
.unwrap_or_default(),
value
.organization
.as_ref()
.map(|s| format!("- organization: {}", s))
.unwrap_or_default()
),
Metadata::Copyright(value) => format!(
"copyright:\n- year: {}{}{}",
value.year,
value
.organization
.as_ref()
.map(|s| format!("- organization: {}\n", s))
.unwrap_or_default(),
value
.comment
.as_ref()
.map(|s| format!("- comment: {}", s))
.unwrap_or_default()
),
Metadata::Date(value) => self.yaml_one(value),
Metadata::Keywords(value) => self.yaml_one(&format!("[{}]", value.join(", "))),
Metadata::Revision(value) => value.to_string(),
Metadata::Status(value) => self.yaml_one(value),
Metadata::Title(value) => self.yaml_one(value),
Metadata::Other(value) => self.yaml_one(&value.value),
}
}
fn yaml_one(&self, value: &str) -> String {
format!("{}: {}", self.key(), value.to_string())
}
}