use minijinja::{Environment, path_loader};
use serde_json::Value;
use std::{fs::read_to_string, ffi::OsStr, path::Path};
use crate::{
utils::console,
validate::check_against_schema
};
use anyhow::{bail, Result};
pub fn render_template(template: &Path, data: &Path) -> Result<String> {
if !data.is_file() {
bail!("Modelcard file '{}' does not exist.", data.display());
}
let data = crate::utils::load_json_file(data)?;
render_value_to_template(data, Some(template))
}
pub fn render_template_valid(template: &Path, data: &Path, schema: &Path) -> Result<String> {
if !data.is_file() {
bail!("Modelcard file '{}' does not exist.", data.display());
}
if !schema.is_file() {
bail!("Schema file '{}' does not exist.", schema.display());
}
if let Err(e) = check_against_schema(schema, data) {
bail!("Project could not be validated!\n{:?}", e);
}
let data = crate::utils::load_json_file(data)?;
render_value_to_template(data, Some(template))
}
pub fn render_value_to_template(data: Value, template: Option<&Path>) -> Result<String> {
let template_name: &str;
let template_content: String;
match template {
None => {
template_name = "default_md";
template_content = crate::assets::templates::get_md().to_string();
}
Some(t) => {
if !t.is_file() {
bail!("Template file does not exist at '{}'", t.display());
}
template_name = t.file_name()
.unwrap_or(OsStr::new("modelcard.md.jinja"))
.to_str()
.unwrap_or("modelcard.md.jinja");
template_content = read_to_string(t)
.map_err(|e| anyhow::anyhow!("Failed to read template file: {}", e))?;
}
}
console::debug("Rendering template...");
console::debug(&format!("Template: {}", template_name));
let mut env = Environment::new();
env.add_template(template_name, template_content.as_str())?;
let template = env.get_template(template_name)?;
match template.render(&data) {
Ok(rendered) => {
console::debug("Done!");
Ok(rendered)
},
Err(e) => bail!("Could not render template: {:?}", e),
}
}
#[allow(dead_code)]
#[doc(hidden)]
fn create_env(path: &Path) -> Environment<'static> {
let mut env = Environment::new();
env.set_loader(path_loader(path));
env
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::create_file;
use std::{
env::temp_dir,
fs::{create_dir, remove_dir_all},
path::PathBuf
};
fn get_temp_dir(path: &str, create: bool) -> PathBuf {
let mut dir = temp_dir();
dir.push(path);
if dir.try_exists().expect("Could not check test directory") {
remove_dir_all(&dir).expect("Could not free test directory");
}
if create {
create_dir(&dir).expect("Could not create test directory");
}
dir
}
fn setup_test_environment(name: &str, template_content: &str, data_content: &str, schema_content: &str) -> (PathBuf, PathBuf, PathBuf) {
let dir = get_temp_dir(name, true);
let template_path = dir.join("template.md.jinja");
let data_path = dir.join("data.json");
let schema_path = dir.join("schema.json");
create_file(template_path.as_path(), template_content).expect("Could not create template file");
create_file(data_path.as_path(), data_content).expect("Could not create data file");
create_file(schema_path.as_path(), schema_content).expect("Could not create data file");
(template_path, data_path, schema_path)
}
#[test]
fn test_render_template_valid() {
let (template_path, data_path, schema_path) = setup_test_environment(
"render_template_valid",
"Hello, {{ name }}!",
r#"{"name": "World"}"#,
r#"{"type": "object", "properties": {"name": {"type": "string"}}}"#,
);
let result = render_template_valid(&template_path, &data_path, &schema_path);
assert!(result.is_ok());
assert_eq!(result.expect("Failed to render template"), "Hello, World!");
}
#[test]
fn test_render_template_with_missing_data() {
let (template_path, _, schema_path) = setup_test_environment(
"render_template_with_missing_data",
"Hello, {{ name }}!",
r#"{"name": "World"}"#,
r#"{"type": "object", "properties": {"name": {"type": "string"}}}"#,
);
let missing_data_path = Path::new("nonexistent_data.json");
let result = render_template_valid(&template_path, &missing_data_path, &schema_path);
assert!(result.is_err());
}
#[test]
fn test_render_template_with_invalid_schema() {
let (template_path, data_path, _) = setup_test_environment(
"render_template_with_invalid_schema",
"Hello, {{ name }}!",
r#"{"name": "World"}"#,
r#"{"type": "object", "properties": {"name": {"type": "number"}}}"#,
);
let schema_path = Path::new("invalid_schema.json");
let result = render_template_valid(&template_path, &data_path, &schema_path);
assert!(result.is_err());
}
#[test]
fn test_render_value_to_template_with_default_template() {
let content = crate::assets::schema::get_sample();
let data = serde_json::from_str(&content).unwrap();
let result = render_value_to_template(data, None);
assert!(result.is_ok());
}
#[test]
fn test_render_value_to_template_with_custom_template() {
let (template_path, data_path, _) = setup_test_environment(
"render_value_to_template_with_custom_template",
"Goodbye, {{ name }}!",
r#"{"name": "World"}"#,
"",
);
let data = crate::utils::load_json_file(&data_path).unwrap();
let result = render_value_to_template(data, Some(&template_path));
assert!(result.is_ok());
assert_eq!(result.expect("Failed to render template"), "Goodbye, World!");
}
}