use crate::parsing::placeholders::BibEntry; use std::collections::HashMap; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; pub const SECTION: &str = "section"; pub const PARAGRAPH: &str = "paragraph"; pub const LIST: &str = "list"; pub const TABLE: &str = "table"; pub const CODE_BLOCK: &str = "code_block"; pub const QUOTE: &str = "quote"; pub const IMPORT: &str = "import"; #[derive(Clone, Debug)] pub enum MetadataValue { String(String), Integer(i64), Float(f64), Bool(bool), Placeholder(Arc>), } #[derive(Clone, Debug)] pub enum Element { Block(Box), Line(Box), Inline(Box), } #[derive(Clone, Debug)] pub enum Block { Section(Section), Paragraph(Paragraph), List(List), Table(Table), CodeBlock(CodeBlock), Quote(Quote), Import(Import), Placeholder(Arc>), } #[derive(Clone, Debug)] pub enum Line { Text(TextLine), Ruler(Ruler), Anchor(Anchor), Centered(Centered), ReferenceEntry(ReferenceEntry), } #[derive(Clone, Debug)] pub struct Document { pub elements: Vec, pub(crate) is_root: bool, pub(crate) path: Option, pub(crate) placeholders: Vec>>, pub bib_entries: HashMap>>, pub config_settings: HashMap>>, } #[derive(Clone, Debug)] pub struct Section { pub(crate) header: Header, pub(crate) elements: Vec, pub(crate) metadata: Option, } #[derive(Clone, Debug)] pub struct Header { pub(crate) size: u8, pub(crate) line: Line, pub(crate) anchor: String, } #[derive(Clone, Debug)] pub struct Paragraph { pub(crate) elements: Vec, } #[derive(Clone, Debug)] pub struct List { pub(crate) ordered: bool, pub items: Vec, } #[derive(Clone, Debug)] pub struct ListItem { pub(crate) text: Line, pub(crate) level: u16, pub(crate) ordered: bool, pub(crate) children: Vec, } #[derive(Clone, Debug)] pub struct Table { pub(crate) header: Row, pub(crate) rows: Vec, } #[derive(Clone, Debug)] pub struct Row { pub(crate) cells: Vec, } #[derive(Clone, Debug)] pub struct Cell { pub(crate) text: Line, } #[derive(Clone, Debug)] pub struct CodeBlock { pub(crate) language: String, pub(crate) code: String, } #[derive(Clone, Debug)] pub struct Quote { pub(crate) metadata: Option, pub(crate) text: Vec, } #[derive(Clone, Debug)] pub struct Import { pub(crate) path: String, pub(crate) anchor: Arc>, } #[derive(Clone, Debug)] pub struct ImportAnchor { pub(crate) document: Option, } #[derive(Clone, Debug)] pub struct InlineMetadata { pub(crate) data: HashMap, } #[derive(Clone, Debug)] pub struct Ruler {} #[derive(Clone, Debug)] pub struct TextLine { pub subtext: Vec, } #[derive(Clone, Debug)] pub enum Inline { Plain(PlainText), Bold(BoldText), Italic(ItalicText), Underlined(UnderlinedText), Striked(StrikedText), Monospace(MonospaceText), Superscript(SuperscriptText), Url(Url), Image(Image), Placeholder(Arc>), Reference(Reference), Checkbox(Checkbox), Emoji(Emoji), Colored(Colored), } #[derive(Clone, Debug)] pub struct PlainText { pub(crate) value: String, } #[derive(Clone, Debug)] pub struct BoldText { pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct ItalicText { pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct UnderlinedText { pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct StrikedText { pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct MonospaceText { pub(crate) value: String, } #[derive(Clone, Debug)] pub struct SuperscriptText { pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct Checkbox { pub(crate) value: bool, } #[derive(Clone, Debug)] pub struct Url { pub description: Option, pub url: String, } #[derive(Clone, Debug)] pub struct Image { pub(crate) url: Url, pub(crate) metadata: Option, } #[derive(Clone, Debug)] pub struct Placeholder { pub(crate) name: String, pub(crate) value: Option, pub(crate) metadata: Option, } #[derive(Clone, Debug)] pub struct Anchor { pub(crate) description: Box, pub(crate) reference: String, } #[derive(Clone, Debug)] pub struct Centered { pub(crate) line: TextLine, } #[derive(Clone, Debug)] pub struct Reference { pub(crate) value: Option, pub(crate) metadata: Option, pub(crate) display: Option>>, } #[derive(Clone, Debug)] pub struct ReferenceEntry { pub(crate) value: Option, pub(crate) reference_count: usize, } #[derive(Clone, Debug)] pub enum RefValue { BibEntry(Arc>), } #[derive(Clone, Debug)] pub struct ConfigValue { pub(crate) value: MetadataValue, } #[derive(Clone, Debug)] pub struct Emoji { pub(crate) value: char, pub(crate) name: String, } #[derive(Clone, Debug)] pub struct Colored { pub(crate) value: Box, pub(crate) color: String, } // implementations impl Document { fn get_default_config() -> HashMap>> { let mut config: HashMap>> = HashMap::new(); config.insert( "bib-display".to_string(), Arc::new(Mutex::new(ConfigValue { value: MetadataValue::String( "{{title}} - {{author}} - {{date}} - {{url}} - ({{notes}})".to_string(), ), })), ); config.insert( "ref-display".to_string(), Arc::new(Mutex::new(ConfigValue { value: MetadataValue::String("[{{key}}]".to_string()), })), ); config } pub fn new(is_root: bool) -> Self { Self { elements: Vec::new(), is_root, path: None, placeholders: Vec::new(), bib_entries: HashMap::new(), config_settings: Self::get_default_config(), } } pub fn add_element(&mut self, element: Block) { self.elements.push(element) } pub fn add_placeholder(&mut self, placeholder: Arc>) { self.placeholders.push(placeholder); } pub fn create_toc(&self, ordered: bool) -> List { let mut list = List::new(); list.ordered = ordered; self.elements.iter().for_each(|e| match e { Block::Section(sec) => { if !sec.get_hide_in_toc() { let mut item = ListItem::new(Line::Anchor(sec.header.get_anchor()), 1, ordered); item.children.append(&mut sec.get_toc_list(ordered).items); list.add_item(item); } } Block::Import(imp) => { let anchor = imp.anchor.lock().unwrap(); if let Some(doc) = &anchor.document { list.items.append(&mut doc.create_toc(ordered).items) } } _ => {} }); list } pub fn set_config_param( &mut self, key: String, value: ConfigValue, ) -> Option>> { if let Some(arc_val) = self.config_settings.get(&key) { arc_val.lock().unwrap().set_value(value.value); Some(Arc::clone(&arc_val)) } else { self.config_settings .insert(key, Arc::new(Mutex::new(value))) } } pub fn get_config_param(&self, key: &str) -> Option>> { if let Some(val) = self.config_settings.get(key) { Some(Arc::clone(val)) } else { None } } /// Processes section and import elements /// /// if it encounters a section it checks if the sections is of smaller order than the previous one /// if thats the case it grabs the previous one and adds the section to its children /// /// if it encounters an import, it loads the imports top elements to its own pub fn postprocess_imports(&mut self) { let mut new_order: Vec = Vec::with_capacity(self.elements.len()); self.elements.reverse(); let mut count: usize = 0; let mut last_section: Option<(u8, usize)> = None; while let Some(element) = self.elements.pop() { match element { Block::Section(sec) => { if let Some((last_size, last_pos)) = last_section { if sec.header.size > last_size { let last = new_order.get_mut(last_pos).unwrap(); if let Block::Section(p_sec) = last { p_sec.add_section(sec); continue; } } } last_section = Some((sec.header.size, count)); new_order.push(Block::Section(sec)); } Block::Import(imp) => { let arc_anchor = Arc::clone(&imp.anchor); let anchor = &mut arc_anchor.lock().unwrap(); if let Some(doc) = &mut anchor.document { self.placeholders.append(&mut doc.placeholders); doc.elements.reverse(); self.elements.append(&mut doc.elements); anchor.document = None; continue; } else { new_order.push(Block::Import(imp)); } } _ => { if let Some((_, last_pos)) = last_section { let last = new_order.get_mut(last_pos).unwrap(); if let Block::Section(p_sec) = last { p_sec.add_element(element); continue; } } new_order.push(element) } } count += 1; } self.elements = new_order; } } impl ConfigValue { fn set_value(&mut self, value: MetadataValue) { self.value = value; } } impl Section { pub fn new(header: Header) -> Self { Self { header, elements: Vec::new(), metadata: None, } } pub fn add_element(&mut self, element: Block) { self.elements.push(element) } pub fn get_toc_list(&self, ordered: bool) -> List { let mut list = List::new(); self.elements.iter().for_each(|e| { if let Block::Section(sec) = e { if !sec.get_hide_in_toc() { let mut item = ListItem::new(Line::Anchor(sec.header.get_anchor()), 1, ordered); item.children.append(&mut sec.get_toc_list(ordered).items); list.add_item(item); } } }); list } pub(crate) fn get_hide_in_toc(&self) -> bool { if let Some(meta) = &self.metadata { meta.get_bool("toc-hidden") } else { false } } /// adds a child section to the section /// It either adds it directly to its elements or iterates through its children to /// add it to the fitting one pub(crate) fn add_section(&mut self, section: Section) { if section.header.size == self.header.size + 1 { self.elements.push(Block::Section(section)) } else { let has_parent = Mutex::new(AtomicBool::new(true)); let iterator = self.elements.iter_mut().rev().filter(|e| { if let Block::Section(sec) = e { if sec.header.size > section.header.size { has_parent.lock().unwrap().store(true, Ordering::Relaxed); true } else { false } } else { false } }); if has_parent.lock().unwrap().load(Ordering::Relaxed) { for sec in iterator { if let Block::Section(sec) = sec { if sec.header.size < section.header.size { sec.add_section(section); break; } } } } else { self.elements.push(Block::Section(section)); } } } } impl Header { pub fn new(content: Line, anchor: String) -> Self { Self { size: 0, anchor, line: content, } } pub fn get_anchor(&self) -> Anchor { Anchor { description: Box::new(self.line.clone()), reference: self.anchor.clone(), } } } impl Paragraph { pub fn new() -> Self { Self { elements: Vec::new(), } } pub fn add_element(&mut self, element: Line) { self.elements.push(element) } } impl List { pub fn new() -> Self { Self { ordered: false, items: Vec::new(), } } pub fn add_item(&mut self, item: ListItem) { self.items.push(item) } } impl ListItem { pub fn new(text: Line, level: u16, ordered: bool) -> Self { Self { text, level, ordered, children: Vec::new(), } } pub fn add_child(&mut self, child: ListItem) { self.children.push(child) } } impl TextLine { pub fn new() -> Self { Self { subtext: Vec::new(), } } pub fn add_subtext(&mut self, subtext: Inline) { self.subtext.push(subtext) } } impl Table { pub fn new(header: Row) -> Self { Self { header, rows: Vec::new(), } } pub fn add_row(&mut self, row: Row) { self.rows.push(row) } } impl Row { pub fn new() -> Self { Self { cells: Vec::new() } } pub fn add_cell(&mut self, cell: Cell) { self.cells.push(cell) } } impl Url { pub fn new(description: Option, url: String) -> Self { Self { description, url } } } impl Quote { pub fn new(metadata: Option) -> Self { Self { metadata, text: Vec::new(), } } pub fn add_text(&mut self, text: TextLine) { self.text.push(text) } } impl ImportAnchor { pub fn new() -> Self { Self { document: None } } pub fn set_document(&mut self, document: Document) { self.document = Some(document); } } impl PartialEq for Import { fn eq(&self, other: &Self) -> bool { self.path == other.path } } impl Placeholder { pub fn new(name: String, metadata: Option) -> Self { Self { name, value: None, metadata, } } pub fn set_value(&mut self, value: Element) { self.value = Some(value); } } impl InlineMetadata { pub fn get_bool(&self, key: &str) -> bool { if let Some(MetadataValue::Bool(value)) = self.data.get(key) { *value } else { false } } pub fn get_string(&self, key: &str) -> Option { if let Some(MetadataValue::String(value)) = self.data.get(key) { Some(value.clone()) } else { None } } } impl RefValue { pub fn get_ref_id(&self) -> String { match self { RefValue::BibEntry(bib) => { let bib = bib.lock().unwrap(); let mut key = bib.key.clone(); key.retain(|c| !c.is_whitespace()); key } } } }