1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use crate::{
    config::{LanguageConfig, OutputFile},
    gen::{to_json, CodeGen},
    render::Renderer,
    Bytes, Error, JsonValue, ParamMap, Result,
};
use atelier_core::model::Model;
use std::{
    collections::{BTreeMap, BTreeSet},
    path::Path,
};

// page name for doc template
const DOC_TEMPLATE: &str = "namespace_doc";

/// Default templates
pub const HTML_TEMPLATES: &[(&str, &str)] = &[
    ("page_base", include_str!("../templates/html/page_base.hbs")),
    (
        "namespace_doc",
        include_str!("../templates/html/namespace_doc.hbs"),
    ),
];

#[derive(Debug, Default)]
pub(crate) struct DocGen {}

impl CodeGen for DocGen {
    /// Initialize code generator and renderer for language output.j
    /// This hook is called before any code is generated and can be used to initialize code generator
    /// and/or perform additional processing before output files are created.
    fn init(
        &mut self,
        model: Option<&Model>,
        lc: &LanguageConfig,
        output_dir: &Path,
        renderer: &mut Renderer,
    ) -> std::result::Result<(), Error> {
        let model = match model {
            None => return Ok(()),
            Some(model) => model,
        };
        for t in HTML_TEMPLATES.iter() {
            renderer.add_template(*t)?;
        }
        let mut params: BTreeMap<String, JsonValue> = to_json(&lc.parameters)?;
        let json_model = atelier_json::model_to_json(model);
        params.insert("model".to_string(), json_model);

        let minified = match params.get("minified") {
            Some(JsonValue::Bool(b)) => *b,
            _ => false,
        };
        params.insert("minified".to_string(), JsonValue::Bool(minified));
        let doc_template = match params.get("doc_template") {
            Some(JsonValue::String(s)) => s.clone(),
            _ => DOC_TEMPLATE.to_string(),
        };

        // renderer is already initialized with "model" as the json-ast model,
        // but Model has a more convenient way to get namespaces.
        // Get list of namespaces from top level shapes, using BTreeSet to remove duplicates
        let namespaces = model
            .namespaces()
            .iter()
            .map(|id| id.to_string())
            .collect::<BTreeSet<String>>();

        std::fs::create_dir_all(&output_dir).map_err(|e| {
            Error::Io(format!(
                "creating directory {}: {}",
                output_dir.display(),
                e
            ))
        })?;

        for ns in namespaces.iter() {
            let output_file =
                output_dir.join(format!("{}.html", crate::strings::to_snake_case(ns)));

            let mut out = std::fs::File::create(&output_file).map_err(|e| {
                Error::Io(format!(
                    "writing output file {}: {}",
                    output_file.display(),
                    e
                ))
            })?;
            params.insert("namespace".to_string(), JsonValue::String(ns.clone()));
            params.insert("title".to_string(), JsonValue::String(ns.clone()));
            renderer.render(&doc_template, &params, &mut out)?;
        }

        Ok(())
    }

    /// DocGen doesn't do per-file generation so this is a no-op
    fn generate_file(
        &mut self,
        _model: &Model,
        _file_config: &OutputFile,
        _params: &ParamMap,
    ) -> Result<Bytes> {
        Ok(Bytes::new())
    }

    // never called
    #[doc(hidden)]
    fn get_file_extension(&self) -> &'static str {
        "html"
    }
}