//mod types; use crate::entities::*; use pandoc_types::definition::{Attr, Block, Inline, Meta, MetaValue, Pandoc}; use std::collections::HashMap; impl Described { pub fn into_pandoc( self, descriptions: &HashMap, ) -> (Pandoc, HashMap) { let mut meta = Meta::null(); let title = self.title(); let mut content_before = self.content_before(&descriptions); let mut content_after = self.content_after(&descriptions); let leftovers = self.leftovers(); meta.0.insert( "title".to_string(), MetaValue::MetaString(self.description.name), ); let mut content = Vec::new(); content.push(Block::Header(1, Attr::null(), title)); content.append(&mut content_before); if !self.description.detailed.is_empty() { content.push(Block::Header( 2, Attr::null(), vec![Inline::Str(String::from("Description"))], )); content.push(Block::Div( Attr(String::new(), vec![String::from("doc")], vec![]), vec![raw_markdown(self.description.detailed)], )); } content.append(&mut content_after); (Pandoc(meta, content), leftovers) } } // TODO: replace with single function so we can move out, and remove all of those clones trait PandocDisplay { fn content_before(&self, _descriptions: &HashMap) -> Vec { vec![] } fn content_after(&self, _descriptions: &HashMap) -> Vec { vec![] } fn leftovers(&self) -> HashMap { HashMap::new() } } impl Described { fn title(&self) -> Vec { match &self.entity { Entity::Variable(variable) => vec![Inline::Code( Attr(String::new(), vec![String::from("cpp")], vec![]), variable.r#type.clone() + " " + &self.description.name, )], _ => vec![Inline::Code(Attr::null(), self.description.name.clone())], } } } impl PandocDisplay for Described { fn content_before(&self, descriptions: &HashMap) -> Vec { match &self.entity { Entity::NameSpace(ns) => ns.content_before(descriptions), Entity::Variable(var) => var.content_before(descriptions), Entity::Function(func) => func.content_before(descriptions), Entity::Class(class) => class.content_before(descriptions), } } fn content_after(&self, descriptions: &HashMap) -> Vec { match &self.entity { Entity::NameSpace(ns) => ns.content_after(descriptions), Entity::Variable(var) => var.content_after(descriptions), Entity::Function(func) => func.content_after(descriptions), Entity::Class(class) => class.content_after(descriptions), } } fn leftovers(&self) -> HashMap { match &self.entity { Entity::NameSpace(ns) => ns.leftovers(), Entity::Variable(var) => var.leftovers(), Entity::Function(func) => func.leftovers(), Entity::Class(class) => class.leftovers(), } } } impl PandocDisplay for NameSpace { fn content_after(&self, descriptions: &HashMap) -> Vec { let mut content = Vec::new(); content.push(Block::Header( 2, Attr::null(), vec![Inline::Str("Members".to_string())], )); if let Some(member_list) = member_list(self.members.keys(), descriptions) { content.push(member_list); } else { content.push(str_block(String::from("None"))); } content } fn leftovers(&self) -> HashMap { self.members.clone() } } impl PandocDisplay for Class { fn content_after(&self, descriptions: &HashMap) -> Vec { let mut content = Vec::new(); if let Some(member_types) = member_list(&self.member_types, descriptions) { content.push(Block::Header( 2, Attr::null(), vec![Inline::Str("Member Types".to_string())], )); content.push(member_types); } if let Some(member_functions) = member_list(self.member_functions.keys(), descriptions) { content.push(Block::Header( 2, Attr::null(), vec![Inline::Str("Member Functions".to_string())], )); content.push(member_functions); } if let Some(member_variables) = member_list(self.member_variables.keys(), descriptions) { content.push(Block::Header( 2, Attr::null(), vec![Inline::Str("Member Variables".to_string())], )); content.push(member_variables); } content } fn leftovers(&self) -> HashMap { self.member_functions .iter() .map(|(usr, func)| (usr.clone(), Entity::from(func.clone()))) .chain( self.member_variables .iter() .map(|(usr, var)| (usr.clone(), Entity::from(var.clone()))), ) .collect() } } impl PandocDisplay for Variable {} impl PandocDisplay for Function {} fn str_block(content: String) -> Block { Block::Plain(vec![Inline::Str(content)]) } fn entity_link(usr: &Usr, name: String) -> Inline { use pandoc_types::definition::Target; use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; use std::iter::once; // https://url.spec.whatwg.org/#fragment-percent-encode-set const FRAGMENT: &AsciiSet = &CONTROLS .add(b' ') .add(b'"') .add(b'<') .add(b'>') .add(b'`') .add(b'#') .add(b'?') .add(b'{') .add(b'}'); Inline::Link( Attr::null(), vec![Inline::Code(Attr::null(), name)], Target( once("./") .chain(utf8_percent_encode(&usr.0, FRAGMENT)) .collect(), String::new(), ), ) } fn raw_markdown(text: String) -> Block { use pandoc_types::definition::Format; Block::RawBlock(Format(String::from("markdown")), text) } fn member_list<'a>( members: impl IntoIterator, descriptions: &HashMap, ) -> Option { let definitions: Vec<(Vec, Vec>)> = members .into_iter() .filter_map(|usr| { let name = &descriptions.get(usr)?.name; Some(( vec![entity_link(usr, name.clone())], vec![vec![str_block( descriptions.get(usr).unwrap().brief.clone(), )]], )) }) .collect(); if definitions.is_empty() { None } else { Some(Block::DefinitionList(definitions)) } }