use crate::config::Format;
use anyhow::Result;
use std::collections::HashMap;
use dyn_clone::DynClone;
use cowstr::CowStr;
use linked_hash_map::LinkedHashMap;
use std::fmt::Debug;
use std::io::Write;
use crate::parser::ParserSettings;
use crate::renderers::extensions::RenderExtension;
use crate::renderers::parameter_resolution::ParameterResolution;
use crate::renderers::references::ReferenceVisitor;
use cdoc_parser::ast::visitor::AstVisitor;
use cdoc_parser::ast::{Ast, Reference};
use cdoc_parser::document::Document;
use cdoc_parser::notebook::NotebookMeta;
use tera::Context;
use crate::templates::TemplateManager;
pub mod extensions;
pub mod generic;
pub mod json;
pub mod notebook;
mod parameter_resolution;
mod references;
pub type RenderResult = CowStr;
pub struct RenderContext<'a> {
pub doc: &'a mut Document<Ast>,
pub templates: &'a TemplateManager,
pub extra_args: Context,
pub notebook_output_meta: &'a NotebookMeta,
pub format: &'a dyn Format,
pub parser_settings: ParserSettings,
pub references: LinkedHashMap<String, Reference>,
pub references_by_type: HashMap<String, Vec<(String, Reference)>>,
}
impl<'a> RenderContext<'a> {
pub fn new(
doc: &'a mut Document<Ast>,
templates: &'a TemplateManager,
mut extra_args: Context,
notebook_output_meta: &'a NotebookMeta,
format: &'a dyn Format,
parser_settings: ParserSettings,
) -> Result<Self> {
let mut parameter_resolution = ParameterResolution { templates };
parameter_resolution.walk_ast(&mut doc.content.blocks)?;
let mut ref_visit = ReferenceVisitor::new();
ref_visit.walk_ast(&mut doc.content.blocks)?;
let rbt = references_by_type(&mut ref_visit.references);
extra_args.insert("refs", &ref_visit.references);
extra_args.insert("refs_by_type", &rbt);
extra_args.insert("defs", &templates.definitions);
Ok(RenderContext {
doc,
templates,
extra_args,
notebook_output_meta,
format,
parser_settings,
references: ref_visit.references,
references_by_type: rbt,
})
}
}
pub fn references_by_type(
refs: &mut LinkedHashMap<String, Reference>,
) -> HashMap<String, Vec<(String, Reference)>> {
let mut type_map = HashMap::new();
for (id, reference) in refs {
type_map
.entry(reference.obj_type.to_string())
.or_insert(vec![])
.push((id.to_string(), reference.clone()));
reference.num = type_map.get(&reference.obj_type).unwrap().len();
}
type_map
}
#[typetag::serde]
pub trait DocumentRenderer: DynClone + Debug + Send + Sync {
fn render_doc(
&mut self,
ctx: &mut RenderContext,
extensions: Vec<Box<dyn RenderExtension>>,
) -> Result<Document<RenderResult>>;
}
dyn_clone::clone_trait_object!(DocumentRenderer);
pub trait RenderElement<T> {
fn render(&mut self, elem: &T, ctx: &RenderContext, buf: impl Write) -> Result<()>;
fn render_inner(&mut self, elem: &T, ctx: &RenderContext) -> Result<CowStr> {
let mut buf = Vec::new();
self.render(elem, ctx, &mut buf)?;
Ok(String::from_utf8(buf)?.into())
}
}
impl<T: RenderElement<R>, R> RenderElement<Vec<R>> for T {
fn render(&mut self, elem: &Vec<R>, ctx: &RenderContext, mut buf: impl Write) -> Result<()> {
elem.iter().try_for_each(|e| self.render(e, ctx, &mut buf))
}
}
pub struct RenderedParam {
pub key: Option<CowStr>,
pub value: CowStr,
}