1use serde::Deserialize;
2use serde_json::Value;
3use std::{collections::HashMap, rc::Rc};
4use tracing::{error, info};
5
6pub mod custom;
7pub mod errors;
8pub(crate) mod filesystem;
9pub mod helpers;
10pub mod loader;
11#[cfg(feature = "bin")]
12pub mod opt;
13pub mod processor;
14pub mod render;
15pub mod resolver;
16
17use filesystem::save_file_content;
18use handlebars::Handlebars;
19use thiserror::Error;
20
21type OptionsMap = HashMap<String, Options>;
22
23type OriginalDocumentsHash = HashMap<loader::DocumentPath, Rc<Value>>;
24type ResolvedDocumentsHash = HashMap<loader::DocumentPath, Rc<Value>>;
25type HandlebarsHash<'a> = HashMap<HandlebarsReusableConf, (String, Handlebars<'a>)>;
26
27#[derive(Debug, PartialEq, Eq, Hash)]
28struct HandlebarsReusableConf {
29 pub templates: Vec<String>,
30 pub custom_helpers: Vec<String>,
31}
32
33#[derive(Error, Debug)]
34pub enum SaverError {
35 #[error("Io Error: `{0}`.")]
36 Io(#[from] std::io::Error),
37}
38
39#[derive(Debug, Deserialize)]
40pub struct Options {
41 pub source: String,
42 pub output: String,
43 pub templates: Vec<String>,
44 pub intermediate: Option<String>,
45 pub custom_helpers: Vec<String>,
46 pub global_parameters: HashMap<String, serde_json::Value>,
47}
48
49#[::tracing::instrument(level = "trace")]
50pub fn run_all_codegenr(options_map: OptionsMap) -> Result<(), errors::CodegenrError> {
51 let mut original_cache = Default::default();
52 let mut resolved_cache = Default::default();
53 let mut reusables = Default::default();
54 for (name, options) in options_map {
55 info!("Running code generation section `{}`", name);
56 if let Err(e) = run_codegenr(options, &mut original_cache, &mut resolved_cache, &mut reusables) {
57 error!("Error while executing the `{}` section: `{}`.", name, e);
58 }
59 }
60 Ok(())
61}
62
63#[::tracing::instrument(level = "trace")]
64pub fn run_one_codegenr(options: Options) -> Result<(), errors::CodegenrError> {
65 let mut original_cache = Default::default();
66 let mut resolved_cache = Default::default();
67 let mut reusables = Default::default();
68 run_codegenr(options, &mut original_cache, &mut resolved_cache, &mut reusables)
69}
70
71#[::tracing::instrument(level = "trace")]
72fn run_codegenr(
73 options: Options,
74 original_cache: &mut OriginalDocumentsHash,
75 resolved_cache: &mut ResolvedDocumentsHash,
76 reusables: &mut HandlebarsHash,
77) -> Result<(), errors::CodegenrError> {
78 let document = loader::DocumentPath::parse(&options.source)?;
79 let json = resolver::resolve_refs(document, original_cache, resolved_cache)?;
80
81 if options.intermediate.is_some() {
82 save_intermediate(&options.intermediate, "resolved.json", &format!("{:#}", json))?;
83 }
84
85 let (main_template_name, mut handlebars) = reusables
86 .entry(HandlebarsReusableConf {
87 custom_helpers: options.custom_helpers,
88 templates: options.templates,
89 })
90 .or_insert_with_key(|conf| {
91 let mut all_templates = vec![];
92 for t in conf.templates.iter() {
93 let templates = render::get_templates_from_directory(t).unwrap(); all_templates.extend(templates);
95 }
96 let templates = render::TemplateCollection::from_list(all_templates).unwrap(); let mut handlebars = Handlebars::new();
99 templates.setup_handlebars(&mut handlebars).unwrap(); custom::handlebars_setup(&mut handlebars, &conf.custom_helpers).unwrap(); (templates.main_template_name().to_owned(), handlebars)
102 })
103 .clone();
104
105 helpers::handlebars_setup(&mut handlebars, options.global_parameters);
106
107 let rendered = handlebars.render(&main_template_name, &(*json))?;
108
109 save_intermediate(&options.intermediate, "rendered.txt", &rendered)?;
110
111 processor::process(&rendered, options.output)?;
112 Ok(())
113}
114
115fn save_intermediate(file: &Option<String>, extension: &str, content: &str) -> Result<(), SaverError> {
116 if let Some(s) = file {
117 let full_file_name = format!("{}.{}", s, extension);
118 save_file_content(".", &full_file_name, content)?;
119 }
120 Ok(())
121}