blob: ae376116d627d99c153dc569c7bbdf086469b137 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{Context as _, Error};
use handlebars::Handlebars;
use log::info;
use serde_json::Value;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use crate::templates::{FidldocTemplate, HandlebarsHelper};
pub struct MarkdownTemplate<'a> {
handlebars: Handlebars<'a>,
output_path: PathBuf,
}
impl MarkdownTemplate<'_> {
pub fn new(output_path: &PathBuf) -> MarkdownTemplate<'_> {
// Handlebars
let mut handlebars = Handlebars::new();
// Register partials
for &(name, template, expect) in &[
("header", include_str!("partials/header.hbs"), "Failed to include header"),
("header_dir", include_str!("partials/header_dir.hbs"), "Failed to include header_dir"),
("footer", include_str!("partials/footer.hbs"), "Failed to include footer"),
] {
handlebars.register_template_string(name, template).expect(expect);
}
// Register FIDL declarations partials
for &(name, template, expect) in &[
(
"protocols",
include_str!("partials/declarations/protocols.hbs"),
"Failed to include protocols",
),
(
"structs",
include_str!("partials/declarations/structs.hbs"),
"Failed to include structs",
),
("enums", include_str!("partials/declarations/enums.hbs"), "Failed to include enums"),
(
"tables",
include_str!("partials/declarations/tables.hbs"),
"Failed to include tables",
),
(
"unions",
include_str!("partials/declarations/unions.hbs"),
"Failed to include unions",
),
("bits", include_str!("partials/declarations/bits.hbs"), "Failed to include bits"),
(
"constants",
include_str!("partials/declarations/constants.hbs"),
"Failed to include constants",
),
(
"type_aliases",
include_str!("partials/declarations/type_aliases.hbs"),
"Failed to include type_aliases",
),
] {
handlebars.register_template_string(name, template).expect(expect);
}
// Register FIDL type partials
for &(name, template, expect) in &[
("doc", include_str!("partials/types/doc.hbs"), "Failed to include doc"),
("filename", include_str!("partials/types/filename.hbs"), "Failed to include filename"),
("type", include_str!("partials/types/type.hbs"), "Failed to include type"),
("vector", include_str!("partials/types/vector.hbs"), "Failed to include vector"),
] {
handlebars.register_template_string(name, template).expect(expect);
}
// Register core templates
for &(name, template, expect) in &[
("main", include_str!("main.hbs"), "Failed to include main"),
("interface", include_str!("interface.hbs"), "Failed to include interface"),
("toc", include_str!("toc.hbs"), "Failed to include toc"),
] {
handlebars.register_template_string(name, template).expect(expect);
}
// Register helpers
let helpers: &[(&str, HandlebarsHelper)] = &[
("getLink", crate::templates::get_link_helper),
("rpn", crate::templates::remove_package_name),
("eq", crate::templates::eq),
("pl", crate::templates::package_link),
("rpf", crate::templates::remove_parent_folders),
("sl", crate::templates::source_link),
("docLink", crate::templates::doc_link),
("oneline", crate::templates::one_line),
("pulldown", crate::templates::pulldown),
("methodId", crate::templates::method_id),
];
for &(name, helper) in helpers {
handlebars.register_helper(name, Box::new(helper));
}
MarkdownTemplate { handlebars: handlebars, output_path: output_path.to_path_buf() }
}
}
impl FidldocTemplate for MarkdownTemplate<'_> {
fn render_main_page(&self, main_fidl_json: &Value) -> Result<(), Error> {
// Render main page
let main_page_path = self.output_path.join("README.md");
info!("Generating main page documentation {}", main_page_path.display());
let mut main_page_file = File::create(&main_page_path)
.with_context(|| format!("Can't create {}", main_page_path.display()))?;
let main_page_content =
render_template(&self.handlebars, "main".to_string(), &main_fidl_json)
.with_context(|| format!("Can't render main page {}", main_page_path.display()))?;
main_page_file.write_all(main_page_content.as_bytes())?;
// Render TOC
let toc_path = self.output_path.join("_toc.yaml");
info!("Generating TOC {}", toc_path.display());
let mut toc_file = File::create(&toc_path)
.with_context(|| format!("Can't create TOC {}", toc_path.display()))?;
let toc_content = render_template(&self.handlebars, "toc".to_string(), &main_fidl_json)
.with_context(|| format!("Can't render TOC {}", toc_path.display()))?;
toc_file.write_all(toc_content.as_bytes())?;
Ok(())
}
fn render_interface(&self, package: &str, fidl_json: &Value) -> Result<(), Error> {
// Create a directory for interface files
let output_dir = self.output_path.join(package);
fs::create_dir(&output_dir)?;
info!("Created output directory {}", output_dir.display());
let package_index = output_dir.join("README.md");
info!("Generating package documentation {}", package_index.display());
let mut output_file = File::create(&package_index)
.with_context(|| format!("Can't create {}", package_index.display()))?;
// Render files
let package_content =
render_template(&self.handlebars, "interface".to_string(), &fidl_json)
.with_context(|| format!("Can't render interface {}", package))?;
output_file.write_all(package_content.as_bytes())?;
Ok(())
}
fn name(&self) -> String {
return "Markdown".to_string();
}
}
fn render_template(
handlebars: &Handlebars<'_>,
template_name: String,
fidl_json: &Value,
) -> Result<String, Error> {
let content = handlebars
.render(&template_name, &fidl_json)
.with_context(|| format!("Unable to render template '{}'", template_name))?;
Ok(content)
}
#[cfg(test)]
mod test {
use super::*;
use serde_json::json;
#[test]
fn render_main_page_test() {
let source = include_str!("testdata/README.md");
let mut table_of_contents: Vec<crate::TableOfContentsItem> = vec![];
table_of_contents.push(crate::TableOfContentsItem {
name: "fuchsia.auth".to_string(),
link: "fuchsia.auth/README.md".to_string(),
description: "Fuchsia Auth API".to_string(),
});
table_of_contents.push(crate::TableOfContentsItem {
name: "fuchsia.media".to_string(),
link: "fuchsia.media/README.md".to_string(),
description: "Fuchsia Media API".to_string(),
});
let fidl_config = json!(null);
let declarations: Vec<String> = Vec::new();
let main_fidl_doc = json!({
"table_of_contents": table_of_contents,
"fidldoc_version": "0.0.4",
"config": fidl_config,
"search": declarations,
"url_path": "/",
});
let pb = PathBuf::new();
let template = MarkdownTemplate::new(&pb);
let result = render_template(&template.handlebars, "main".to_string(), &main_fidl_doc)
.expect("Unable to render main template");
assert_eq!(result, source);
}
#[test]
fn render_toc_test() {
let source = include_str!("testdata/_toc.yaml");
let mut table_of_contents: Vec<crate::TableOfContentsItem> = vec![];
table_of_contents.push(crate::TableOfContentsItem {
name: "fuchsia.auth".to_string(),
link: "fuchsia.auth/README.md".to_string(),
description: "Fuchsia Auth API".to_string(),
});
table_of_contents.push(crate::TableOfContentsItem {
name: "fuchsia.media".to_string(),
link: "fuchsia.media/README.md".to_string(),
description: "Fuchsia Media API".to_string(),
});
let fidl_config = json!(null);
let declarations: Vec<String> = Vec::new();
let main_fidl_doc = json!({
"table_of_contents": table_of_contents,
"fidldoc_version": "0.0.4",
"config": fidl_config,
"search": declarations,
"url_path": "/",
});
let pb = PathBuf::new();
let template = MarkdownTemplate::new(&pb);
let result = render_template(&template.handlebars, "toc".to_string(), &main_fidl_doc)
.expect("Unable to render toc template");
assert_eq!(result, source);
}
}