use super::config::Config; use crate::types::Entity; use pandoc_types::definition::{Attr, Block, Inline, Meta, MetaValue, Pandoc}; use std::collections::BTreeMap; pub(crate) fn into_pandoc( entity: Entity, config: &Config, ) -> (Pandoc, BTreeMap) { let mut meta = Meta::null(); let title = vec![Inline::Code(Attr::null(), entity.name.clone())]; meta.0 .insert("title".to_string(), MetaValue::MetaString(entity.name)); let mut content = Vec::new(); content.push(Block::Header(1, Attr::null(), title)); if !entity.documentation.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(entity.documentation.clone())], )); } let mut inline_children = BTreeMap::new(); let mut separate_children = BTreeMap::new(); let entity_kind = entity.kind; for (children_kind, children) in entity.children { if config .inlines .get(&entity.language) .and_then(|lang_inlines| lang_inlines.get(&entity_kind)) .map(|children_inlines| children_inlines.contains(&children_kind)) == Some(true) { inline_children.insert(children_kind, children); } else { // By default, do not inline separate_children.insert(children_kind, children); } } for (section_name, children) in &separate_children { if let Some(members_list) = member_list(children) { content.push(Block::Header( 2, Attr::null(), vec![Inline::Str(String::from(section_name))], )); content.push(members_list); } } let mut embedded_documentation = Vec::new(); for (section_name, children) in inline_children { if let Some(members_list) = member_list(&children) { content.push(Block::Header( 2, Attr::null(), vec![Inline::Str(section_name.clone())], )); content.push(members_list); embedded_documentation.push(Block::Header( 2, Attr::null(), vec![Inline::Str(section_name + " Documentation")], )); for (_id, child) in children { embedded_documentation.push(Block::Header( 3, Attr::null(), vec![Inline::Code(Attr::null(), child.name)], )); embedded_documentation.push(Block::Div( Attr(String::new(), vec![String::from("doc")], vec![]), vec![raw_markdown(child.documentation)], )); } } } content.append(&mut embedded_documentation); let leftovers = separate_children .into_iter() .map(|(_section_name, children)| children) .flatten() .collect(); (Pandoc(meta, content), leftovers) } fn str_block(content: String) -> Block { Block::Plain(vec![Inline::Str(content)]) } fn entity_link(id: &str, 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(id, 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) -> Option { let definitions: Vec<(Vec, Vec>)> = members .into_iter() .map(|(id, entity)| { ( vec![entity_link(id, entity.name.clone())], vec![vec![str_block(entity.brief_description.clone())]], ) }) .collect(); if definitions.is_empty() { None } else { Some(Block::DefinitionList(definitions)) } }