1use crate::config::Format;
2use anyhow::Result;
3use std::collections::HashMap;
4
5use dyn_clone::DynClone;
6
7use cowstr::CowStr;
8use linked_hash_map::LinkedHashMap;
9use std::fmt::Debug;
10use std::io::Write;
11
12use crate::parser::ParserSettings;
13use crate::renderers::extensions::RenderExtension;
14use crate::renderers::parameter_resolution::ParameterResolution;
15use crate::renderers::references::ReferenceVisitor;
16use cdoc_parser::ast::visitor::AstVisitor;
17use cdoc_parser::ast::{Ast, Reference};
18use cdoc_parser::document::Document;
19use cdoc_parser::notebook::NotebookMeta;
20use tera::Context;
21
22use crate::templates::TemplateManager;
23
24pub mod extensions;
25pub mod generic;
26pub mod json;
27pub mod notebook;
28mod parameter_resolution;
29mod references;
30
31pub type RenderResult = CowStr;
33
34pub struct RenderContext<'a> {
36 pub doc: &'a mut Document<Ast>,
38 pub templates: &'a TemplateManager,
39 pub extra_args: Context,
41 pub notebook_output_meta: &'a NotebookMeta,
45 pub format: &'a dyn Format,
46 pub parser_settings: ParserSettings,
47 pub references: LinkedHashMap<String, Reference>,
48 pub references_by_type: HashMap<String, Vec<(String, Reference)>>,
49}
50
51impl<'a> RenderContext<'a> {
52 pub fn new(
53 doc: &'a mut Document<Ast>,
54 templates: &'a TemplateManager,
55 mut extra_args: Context,
56 notebook_output_meta: &'a NotebookMeta,
59 format: &'a dyn Format,
60 parser_settings: ParserSettings,
61 ) -> Result<Self> {
62 let mut parameter_resolution = ParameterResolution { templates };
63 parameter_resolution.walk_ast(&mut doc.content.blocks)?;
64
65 let mut ref_visit = ReferenceVisitor::new();
66 ref_visit.walk_ast(&mut doc.content.blocks)?;
67 let rbt = references_by_type(&mut ref_visit.references);
68
69 extra_args.insert("refs", &ref_visit.references);
70 extra_args.insert("refs_by_type", &rbt);
71 extra_args.insert("defs", &templates.definitions);
72 Ok(RenderContext {
77 doc,
78 templates,
79 extra_args,
80 notebook_output_meta,
83 format,
84 parser_settings,
85 references: ref_visit.references,
86 references_by_type: rbt,
87 })
88 }
89}
90
91pub fn references_by_type(
92 refs: &mut LinkedHashMap<String, Reference>,
93) -> HashMap<String, Vec<(String, Reference)>> {
94 let mut type_map = HashMap::new();
95 for (id, reference) in refs {
96 type_map
97 .entry(reference.obj_type.to_string())
98 .or_insert(vec![])
99 .push((id.to_string(), reference.clone()));
100
101 reference.num = type_map.get(&reference.obj_type).unwrap().len();
102 }
103 type_map
104}
105
106#[typetag::serde]
109pub trait DocumentRenderer: DynClone + Debug + Send + Sync {
110 fn render_doc(
111 &mut self,
112 ctx: &mut RenderContext,
113 extensions: Vec<Box<dyn RenderExtension>>,
114 ) -> Result<Document<RenderResult>>;
115}
116
117dyn_clone::clone_trait_object!(DocumentRenderer);
118
119pub trait RenderElement<T> {
121 fn render(&mut self, elem: &T, ctx: &RenderContext, buf: impl Write) -> Result<()>;
123
124 fn render_inner(&mut self, elem: &T, ctx: &RenderContext) -> Result<CowStr> {
128 let mut buf = Vec::new();
129 self.render(elem, ctx, &mut buf)?;
130 Ok(String::from_utf8(buf)?.into())
131 }
132}
133
134impl<T: RenderElement<R>, R> RenderElement<Vec<R>> for T {
137 fn render(&mut self, elem: &Vec<R>, ctx: &RenderContext, mut buf: impl Write) -> Result<()> {
138 elem.iter().try_for_each(|e| self.render(e, ctx, &mut buf))
139 }
140}
141
142pub struct RenderedParam {
143 pub key: Option<CowStr>,
144 pub value: CowStr,
145}