]> git.huck.website - metaforge.git/commitdiff
implemented header block and settings
authorHuck Boles <huck@huck.website>
Tue, 16 May 2023 00:14:10 +0000 (19:14 -0500)
committerHuck Boles <huck@huck.website>
Tue, 16 May 2023 00:14:10 +0000 (19:14 -0500)
src/builder.rs
src/metafile.rs
src/parser.rs

index df25a846adf0532add2ff08c02ca7f35c4ac2af1..b50cb669a50f67fde55bbd6531d37dd27eed05b5 100644 (file)
@@ -1,14 +1,14 @@
-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)?;
@@ -21,14 +21,11 @@ pub fn build_metafile(file: &MetaFile) -> Result<String> {
     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;
 
@@ -56,25 +53,22 @@ fn metafile_to_string(file: &MetaFile) -> Result<String> {
     }
 
     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![]);
@@ -88,44 +82,42 @@ fn get_source_html(file: &MetaFile, opts: &Options) -> Result<String> {
 
 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;
@@ -153,15 +145,6 @@ fn get_variable(key: &str, file: &MetaFile) -> Result<String> {
     }
 }
 
-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
@@ -226,8 +209,11 @@ fn get_max_size(map: &HashMap<String, &[String]>) -> usize {
 #[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();
@@ -236,32 +222,67 @@ mod tests {
         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(())
     }
 }
index cb821da59c8383b906c4151ed56692eb064828f7..05b811f7330197dfbb3507f0ed0f333e4871a521 100644 (file)
@@ -1,4 +1,4 @@
-use crate::{build_metafile, parse_file, write_file, Options};
+use crate::{build_metafile, parse_file, Options};
 use color_eyre::{
     eyre::{bail, eyre},
     Result,
@@ -6,11 +6,49 @@ use color_eyre::{
 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>,
@@ -32,7 +70,7 @@ impl<'a> MetaFile<'a> {
         Self {
             opts,
             path: PathBuf::new(),
-            header: HashMap::new(),
+            header: Header::new(),
             variables: HashMap::new(),
             arrays: HashMap::new(),
             patterns: HashMap::new(),
@@ -40,6 +78,16 @@ impl<'a> MetaFile<'a> {
         }
     }
 
+    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
@@ -65,7 +113,7 @@ impl<'a> MetaFile<'a> {
                 .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());
         }
     }
 
@@ -182,7 +230,7 @@ impl<'a> DirNode<'a> {
             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 {
index 817302ba919e8522ab34b71f1d65f51b9891a904..af6bc7e5a605a75ff037cc591d0b601f093106b2 100644 (file)
@@ -1,4 +1,4 @@
-use crate::{source, MetaFile, Options, Src, Sub};
+use crate::{source, Header, MetaFile, Options, Src, Sub};
 use color_eyre::{eyre::WrapErr, Result};
 use pest::{
     iterators::{Pair, Pairs},
@@ -27,7 +27,7 @@ fn parse_pair<'a>(pair: Pair<Rule>, opts: &'a Options) -> MetaFile<'a> {
         for pair in pair.into_inner() {
             match pair.as_rule() {
                 Rule::source => meta_file.source = parse_source(pair.into_inner()),
-                Rule::header => meta_file.header = parse_defs(pair.into_inner()),
+                Rule::header => meta_file.header = Header::from(parse_defs(pair.into_inner())),
                 Rule::var_def => meta_file.variables = parse_defs(pair.into_inner()),
                 Rule::arr_def => meta_file.arrays = parse_array_defs(pair.into_inner()),
                 Rule::pat_def => meta_file.patterns = parse_defs(pair.into_inner()),