-use crate::{log, parse_file, MetaFile, Options, Src, Sub};
+use crate::{parse_file, MetaFile, Src, Sub};
use color_eyre::{eyre::bail, Result};
use pandoc::{InputFormat, InputKind, OutputFormat, OutputKind, Pandoc, PandocOutput};
-use std::{
- collections::HashMap,
- fs,
- path::{Path, PathBuf},
-};
+use std::{collections::HashMap, fs};
pub fn build_metafile(file: &MetaFile) -> Result<String> {
- let html = get_source_html(file, file.opts)?;
+ if file.header.blank {
+ return Ok(String::new());
+ }
+
+ let html = get_source_html(file)?;
let pattern = get_pattern("base", file)?;
let mut base = parse_file(pattern, file.opts)?;
Ok(output)
}
-pub fn write_file(path: &Path, html: String, opts: &Options) -> Result<()> {
- let dest = find_dest(path, opts)?;
- // want newline to end file
- fs::write(dest, html)?;
- Ok(())
-}
-
fn metafile_to_string(file: &MetaFile) -> Result<String> {
+ if file.header.blank {
+ return Ok(String::new());
+ }
+
let mut output = String::default();
let mut arrays = false;
}
if arrays {
- log!(file.opts, "\t\t\texpanding arrays", 4);
expand_arrays(output, file)
} else {
Ok(output)
}
}
-fn get_source_html(file: &MetaFile, opts: &Options) -> Result<String> {
- log!(opts, "\tbuilding source", 2);
- let file = metafile_to_string(file)?;
+fn get_source_html(file: &MetaFile) -> Result<String> {
+ let string = metafile_to_string(file)?;
- if opts.no_pandoc {
- return Ok(file);
+ if file.opts.no_pandoc || !file.header.pandoc {
+ return Ok(string);
}
- log!(opts, "\t\tcalling pandoc", 3);
let mut pandoc = Pandoc::new();
pandoc
- .set_input(InputKind::Pipe(file))
+ .set_input(InputKind::Pipe(string))
.set_output(OutputKind::Pipe)
.set_input_format(InputFormat::Markdown, vec![])
.set_output_format(OutputFormat::Html, vec![]);
fn get_pattern(key: &str, file: &MetaFile) -> Result<String> {
// SOURCE is already expanded in the initial build_metafile() call
- // we just need to return that
if key == "SOURCE" {
- log!(file.opts, "\t\t\treturning SOURCE", 4);
if let Some(source) = file.patterns.get("SOURCE") {
return Ok(source.to_string());
}
}
- log!(file.opts, format!("\t\tpattern: {}", key), 3);
- // anything not defined should have a default.meta file to fall back to
let mut filename: String;
if let Some(name) = file.get_pat(key) {
filename = name.to_string();
} else {
+ // anything not defined should have a default.meta file to fall back to
filename = "default".to_string()
}
+ // BLANK returns nothing, so no more processing needs to be done
+ if filename == "BLANK" {
+ return Ok(String::from(""));
+ };
+
+ // DEFAULT override for patterns overriding globals
+ if filename == "DEFAULT" {
+ filename = "default".to_string();
+ }
+
// if we're building from base pattern we need to wait on
// parsing/expansion so we can build and convert source to html
// we just want to return the string right now
if key == "base" {
- log!(file.opts, "\t\t\treturning base", 4);
let pattern_path = key.to_string() + "/" + &filename;
let mut path = file.opts.pattern.join(pattern_path);
path.set_extension("meta");
- let base = fs::read_to_string(&path)?;
- return Ok(base);
- }
-
- // BLANK returns nothing, so no more processing needs to be done
- if filename == "BLANK" {
- return Ok(String::from(""));
- };
-
- // DEFAULT override for patterns defined higher in chain
- if filename == "DEFAULT" {
- filename = "default".to_string();
+ return match fs::read_to_string(&path) {
+ Ok(str) => Ok(str),
+ Err(_) => bail!("could not find base file {}", path.display()),
+ };
}
let pattern_path = key.replace('.', "/") + "/" + &filename;
}
}
-fn find_dest(path: &Path, opts: &Options) -> Result<PathBuf> {
- let path = path.canonicalize()?;
-
- let mut path = opts.build.join(path.strip_prefix(&opts.source)?);
- path.set_extension("html");
-
- Ok(path)
-}
-
fn expand_arrays(input: String, file: &MetaFile) -> Result<String> {
let map: HashMap<String, &[String]> = file
.source
#[cfg(test)]
mod tests {
use super::*;
- fn build_options() -> Result<Options> {
- let dir = PathBuf::from("files/site").canonicalize()?;
+ use crate::Options;
+ use std::path::PathBuf;
+
+ fn unit_test(test: &str, result: &str) -> Result<()> {
+ let dir = PathBuf::from("files/test_site").canonicalize()?;
let mut opts = Options::new();
opts.root = dir.clone();
opts.pattern = dir.join("pattern");
opts.clean = true;
- Ok(opts)
+ let test_dir = opts.source.join("unit_tests");
+ let mut file_path = test_dir.join(test);
+ file_path.set_extension("meta");
+ let file = MetaFile::build(file_path, &opts)?;
+
+ let output = build_metafile(&file)?;
+
+ assert_eq!(output, result);
+
+ Ok(())
}
#[test]
fn test_find_dest() -> Result<()> {
- let opts = build_options()?;
- let path = opts.source.join("dir1/dir.meta");
- assert_eq!(find_dest(&path, &opts)?, opts.build.join("dir1/dir.html"));
+ unit_test("find_dest", "<html>\n\n</html>\n")
+ }
+
+ #[test]
+ fn test_blank() -> Result<()> {
+ unit_test("blank/blank_pattern", "")?;
+ unit_test("blank/blank_variable", "<html>\n</html>\n")?;
+ unit_test("blank/blank_array", "<html>\n</html>\n")?;
+ Ok(())
+ }
+
+ #[test]
+ fn test_comment() -> Result<()> {
+ unit_test("blank/comment", "<html>\n\n</html>\n")?;
+ unit_test(
+ "blank/inline_comment",
+ "<html>\n<p>inline comment</p>\n</html>\n",
+ )?;
+ Ok(())
+ }
+
+ #[test]
+ fn test_expand() -> Result<()> {
+ unit_test(
+ "expand/variable_in_source",
+ "<html>\n<p>GOOD</p>\n</html>\n",
+ )?;
+ unit_test("expand/variable_in_pattern", "<html>\nGOOD</html>\n")?;
+ unit_test("expand/array_in_source", "<html>\n<p>12345</p>\n</html>\n")?;
+ unit_test("expand/array_in_pattern", "<html>\n12345</html>\n")?;
+ unit_test("expand/pattern_in_source", "<p>GOOD</p>\n")?;
+ unit_test("expand/pattern_in_pattern", "<html>\nGOOD\nGOOD\n</html>\n")?;
Ok(())
}
#[test]
- fn test_metafile_to_string() -> Result<()> {
- let opts = build_options()?;
- let path = opts.source.join("dir1/sub_dir1/deep2/deep.meta");
- let expanded = "<html><body>GOOD</body></html>";
- assert_eq!(build_metafile(&MetaFile::build(path, &opts)?)?, expanded);
+ fn test_override() -> Result<()> {
+ unit_test("override/variable", "<html>\n<p>GOOD</p>\n</html>\n")?;
+ unit_test("override/pattern", "<html>\nGOOD\nGOOD\n</html>\n")?;
Ok(())
}
#[test]
- fn test_get_pattern() -> Result<()> {
- let opts = build_options()?;
- let file = MetaFile::new(&opts);
- let pat = get_pattern("header", &file)?;
- assert_eq!(pat, "<header>HEADER</header>");
+ #[ignore = "fix global variables"]
+ fn test_global() -> Result<()> {
+ unit_test("global/variable", "GOODGOOD\n")?;
+ unit_test("global/pattern", "GOODGOOD")?;
Ok(())
}
}
-use crate::{build_metafile, parse_file, write_file, Options};
+use crate::{build_metafile, parse_file, Options};
use color_eyre::{
eyre::{bail, eyre},
Result,
use std::collections::HashMap;
use std::{fs, path::PathBuf};
+#[derive(Debug, Clone, Default)]
+pub struct Header {
+ pub blank: bool,
+ pub panic_default: bool,
+ pub panic_undefined: bool,
+ pub filetype: String,
+ pub pandoc: bool,
+}
+
+impl Header {
+ pub fn new() -> Self {
+ Self {
+ blank: false,
+ panic_default: false,
+ panic_undefined: false,
+ filetype: String::from("html"),
+ pandoc: true,
+ }
+ }
+}
+
+impl From<HashMap<String, String>> for Header {
+ fn from(value: HashMap<String, String>) -> Self {
+ let mut header = Header::new();
+ for (key, val) in value.iter() {
+ match &key[..] {
+ "blank" => header.blank = val == "true",
+ "panic_default" => header.panic_default = val == "true",
+ "panic_undefined" => header.panic_undefined = val == "true",
+ "pandoc" => header.pandoc = val == "true",
+ "filetype" => header.filetype = val.to_string(),
+ _ => continue,
+ }
+ }
+ header
+ }
+}
+
#[derive(Debug, Clone)]
pub struct MetaFile<'a> {
pub opts: &'a Options,
pub path: PathBuf,
- pub header: HashMap<String, String>,
+ pub header: Header,
pub variables: HashMap<String, String>,
pub arrays: HashMap<String, Vec<String>>,
pub patterns: HashMap<String, String>,
Self {
opts,
path: PathBuf::new(),
- header: HashMap::new(),
+ header: Header::new(),
variables: HashMap::new(),
arrays: HashMap::new(),
patterns: HashMap::new(),
}
}
+ pub fn dest(&self) -> Result<PathBuf> {
+ let mut path = self
+ .opts
+ .build
+ .join(self.path.strip_prefix(&self.opts.source)?);
+ path.set_extension("html");
+
+ Ok(path)
+ }
+
pub fn name(&self) -> Result<String> {
if self.path.starts_with(&self.opts.source) {
// in source dir, we want the file name without the '.meta' extension
.unwrap_or_default();
Ok(name)
} else {
- color_eyre::eyre::bail!("could not get name from: {:#?}", self);
+ color_eyre::eyre::bail!("could not get name from: {}", self.path.display());
}
}
file.merge(&self.global);
match build_metafile(file) {
Ok(str) => {
- write_file(&file.path, str, file.opts)?;
+ fs::write(file.dest()?, str)?;
}
Err(e) => {
if self.opts.force {