cdoc/renderers/
mod.rs

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
31/// Type alias used to specify that the string is a renderer output.
32pub type RenderResult = CowStr;
33
34/// Context that is passed to the render functions.
35pub struct RenderContext<'a> {
36    /// The document that is being rendered
37    pub doc: &'a mut Document<Ast>,
38    pub templates: &'a TemplateManager,
39    /// Extra arguments (this type is essentially a wrapped HashMap)
40    pub extra_args: Context,
41    /// For syntax highlighting using Syntect
42    // pub syntax_set: &'a SyntaxSet,
43    // theme: &'a Theme,
44    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        // syntax_set: &'a SyntaxSet,
57        // theme: &'a Theme,
58        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        // args.insert("refs", &ctx.references);
73        // println!("{:?}", &ctx.references);
74        // args.insert("refs_by_type", &ctx.references_by_type);
75
76        Ok(RenderContext {
77            doc,
78            templates,
79            extra_args,
80            // syntax_set,
81            // theme,
82            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/// Trait used for rendering a whole document. The trait is used for configuring custom formats in
107/// the courses project.
108#[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
119/// The base trait that renderers should implement for each type used by [create::ast::Ast].
120pub trait RenderElement<T> {
121    /// Render the element to a buffer
122    fn render(&mut self, elem: &T, ctx: &RenderContext, buf: impl Write) -> Result<()>;
123
124    /// Convenience function for creating a buffer, rendering the element into the buffer, and
125    /// returning the result as a string. This is useful when an inner element needs to be rendered
126    /// first to be used in an outer element, hence the name.
127    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
134/// Implementation for vectors of elements. Automatically implemented for any type that implements
135/// the trait.
136impl<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}