]> git.huck.website - metaforge.git/commitdiff
builds dirs
authorHuck Boles <huck@huck.website>
Thu, 11 May 2023 16:27:34 +0000 (11:27 -0500)
committerHuck Boles <huck@huck.website>
Thu, 11 May 2023 16:27:34 +0000 (11:27 -0500)
src/filetype/builder.rs
src/main.rs
src/options.rs
src/tests/test_metafile.rs
test_site/pattern/test/pattern.meta
test_site/source/sub_dir/deep/deep.meta [new file with mode: 0644]
test_site/source/sub_dir/sub_dir.meta [new file with mode: 0644]
tests/metafile_builder.rs

index 982030a43e57b5bffee008c7f8d280b4eaa580a9..f9de49d44cf788dbdac2f8a99ffaed5b12984a22 100644 (file)
@@ -1,4 +1,4 @@
-use crate::{parse_file, MetaFile, Options, Source, Substitution};
+use crate::{log, parse_file, MetaFile, Options, Source, Substitution};
 use color_eyre::{
     eyre::bail,
     eyre::{eyre, WrapErr},
@@ -11,16 +11,19 @@ use std::{
     path::{Path, PathBuf},
 };
 
-pub fn build_metafile(path: &Path, dirs: &Options) -> Result<()> {
-    let file = fs::read_to_string(path)
-        .wrap_err_with(|| eyre!("failed to read: {}\n", path.to_string_lossy()))?;
-    let file = parse_file(&file)
-        .wrap_err_with(|| eyre!("failed to parse: {}\n", path.to_string_lossy()))?;
+pub fn build_metafile(path: &Path, opts: &Options) -> Result<()> {
+    log!(opts, format!("\t{}", path.display()), 1);
+    let file =
+        fs::read_to_string(path).wrap_err_with(|| eyre!("failed to read: {}\n", path.display()))?;
 
-    let html = get_source_html(&file, dirs)
-        .wrap_err_with(|| eyre!("failed converting to html: {}\n", path.to_string_lossy()))?;
+    log!(opts, "\tparsing", 2);
+    let file =
+        parse_file(&file).wrap_err_with(|| eyre!("failed to parse: {}\n", path.display()))?;
 
-    let pattern = get_pattern("base", &file, dirs).wrap_err("failed to get base pattern\n")?;
+    let html = get_source_html(&file, opts)
+        .wrap_err_with(|| eyre!("failed converting to html: {}\n", path.display()))?;
+
+    let pattern = get_pattern("base", &file, opts).wrap_err("failed to get base pattern\n")?;
     let mut base = parse_file(&pattern).wrap_err("failed to parse base pattern\n")?;
 
     base.variables = file.variables;
@@ -29,23 +32,20 @@ pub fn build_metafile(path: &Path, dirs: &Options) -> Result<()> {
 
     base.patterns.insert("SOURCE", &html);
 
-    let output = metafile_to_string(&base, dirs, Some("base"))
-        .wrap_err_with(|| eyre!("failed to build: {}\n", path.to_string_lossy()))?;
+    let output = metafile_to_string(&base, opts, Some("base"))
+        .wrap_err_with(|| eyre!("failed to build: {}\n", path.display()))?;
 
-    let dest = find_dest(path, dirs).wrap_err_with(|| {
-        format!(
-            "could not find destination file: {}\n",
-            path.to_string_lossy()
-        )
-    })?;
+    log!(opts, "\twriting", 2);
+    let dest = find_dest(path, opts)
+        .wrap_err_with(|| format!("could not find destination file: {}\n", path.display()))?;
 
     // want newline to end file
     fs::write(&dest, output + "\n")
-        .wrap_err_with(|| eyre!("could not write to: {}\n", dest.to_string_lossy()))?;
+        .wrap_err_with(|| eyre!("could not write to: {}\n", dest.display()))?;
     Ok(())
 }
 
-pub fn metafile_to_string(file: &MetaFile, dirs: &Options, name: Option<&str>) -> Result<String> {
+pub fn metafile_to_string(file: &MetaFile, opts: &Options, name: Option<&str>) -> Result<String> {
     let mut output = String::default();
     let mut arrays = false;
 
@@ -64,7 +64,7 @@ pub fn metafile_to_string(file: &MetaFile, dirs: &Options, name: Option<&str>) -
                         .filter(|val| *val != "BLANK" && *val != "DEFAULT")
                         .map(|val| val.to_string())
                         .unwrap_or_default(),
-                    Substitution::Pattern(key) => get_pattern(key, file, dirs)
+                    Substitution::Pattern(key) => get_pattern(key, file, opts)
                         .wrap_err_with(|| eyre!("could not find pattern for: {}\n", key))?,
                     // comments have already been removed at this point,
                     // so we use them to mark keys for array substitution
@@ -79,22 +79,26 @@ pub fn metafile_to_string(file: &MetaFile, dirs: &Options, name: Option<&str>) -
     }
 
     if arrays {
+        log!(opts, "\t\t\texpanding arrays", 4);
         expand_arrays(output, file, name)
     } else {
         Ok(output)
     }
 }
 
-fn get_source_html(file: &MetaFile, dirs: &Options) -> Result<String> {
-    let file = metafile_to_string(file, dirs, Some("SOURCE")).wrap_err("failed building source")?;
+fn get_source_html(file: &MetaFile, opts: &Options) -> Result<String> {
+    log!(opts, "\tbuilding source", 2);
+    let file = metafile_to_string(file, opts, Some("SOURCE")).wrap_err("failed building source")?;
     let mut pandoc = Pandoc::new();
 
+    log!(opts, "\t\tsetting up pandoc", 3);
     pandoc
         .set_input(InputKind::Pipe(file))
         .set_output(OutputKind::Pipe)
         .set_input_format(InputFormat::Markdown, vec![])
         .set_output_format(OutputFormat::Html, vec![]);
 
+    log!(opts, "\t\texecuting pandoc command", 3);
     if let Ok(PandocOutput::ToBuffer(html)) = pandoc.execute() {
         Ok(html)
     } else {
@@ -102,14 +106,16 @@ fn get_source_html(file: &MetaFile, dirs: &Options) -> Result<String> {
     }
 }
 
-fn get_pattern(key: &str, file: &MetaFile, dirs: &Options) -> Result<String> {
+fn get_pattern(key: &str, file: &MetaFile, opts: &Options) -> Result<String> {
     // SOURCE is already expanded in the initial build_metafile() call
     // we just need to return that
     if key == "SOURCE" {
+        log!(opts, "\t\t\treturning SOURCE", 4);
         let source = file.patterns.get("SOURCE").unwrap_or(&"");
         return Ok(source.to_string());
     }
 
+    log!(opts, format!("\t\tpattern: {}", key), 3);
     // anything not defined should have a default.meta file to fall back to
     let mut filename = file.get_pat(key).unwrap_or("default");
 
@@ -117,49 +123,56 @@ fn get_pattern(key: &str, file: &MetaFile, dirs: &Options) -> Result<String> {
     // parsing/expansion so we can build and convert source to html
     // we just want to return the string right now
     if key == "base" {
+        log!(opts, "\t\t\treturning base", 4);
         let pattern_path = key.to_string() + "/" + filename;
-        let mut path = dirs.pattern.join(pattern_path);
+        let mut path = opts.pattern.join(pattern_path);
         path.set_extension("meta");
 
         let base = fs::read_to_string(&path)
-            .wrap_err_with(|| eyre!("could not read: {}\n", path.to_string_lossy()))?;
+            .wrap_err_with(|| eyre!("could not read: {}\n", path.display()))?;
         return Ok(base);
     }
 
     // BLANK returns nothing, so no more processing needs to be done
     if filename == "BLANK" {
+        log!(opts, format!("\t\t\treturning blank: {}", key), 4);
         return Ok(String::new());
     };
 
     // DEFAULT override for variables defined higher in chain
     if filename == "DEFAULT" {
+        log!(opts, "\t\t\tdefault pattern", 4);
         filename = "default";
     }
 
+    log!(opts, "\t\t\tbuilding path from key", 4);
     let pattern_path = key.replace('.', "/") + "/" + filename;
-    let mut path = dirs.pattern.join(pattern_path);
+    let mut path = opts.pattern.join(pattern_path);
     path.set_extension("meta");
 
+    log!(opts, "\t\t\tparsing file", 4);
     let pattern = &fs::read_to_string(&path)
-        .wrap_err_with(|| eyre!("could not read: {}\n", path.to_string_lossy()))?;
-    let mut pattern = parse_file(pattern)
-        .wrap_err_with(|| eyre!("could not parse: {}\n", path.to_string_lossy()))?;
+        .wrap_err_with(|| eyre!("could not read: {}\n", path.display()))?;
+    let mut pattern =
+        parse_file(pattern).wrap_err_with(|| eyre!("could not parse: {}\n", path.display()))?;
 
     // copy over maps for expanding contained variables
     pattern.variables = file.variables.clone();
     pattern.arrays = file.arrays.clone();
     pattern.patterns = file.patterns.clone();
 
-    metafile_to_string(&pattern, dirs, Some(key))
+    log!(opts, "\t\t\tbuilding pattern", 4);
+    metafile_to_string(&pattern, opts, Some(key))
 }
 
-fn find_dest(path: &Path, dirs: &Options) -> Result<PathBuf> {
-    let source = dirs.source.to_string_lossy();
-    let build = dirs.build.to_string_lossy();
+fn find_dest(path: &Path, opts: &Options) -> Result<PathBuf> {
+    log!(opts, "\t\tfinding destination", 3);
+    let source = opts.source.to_string_lossy();
+    let build = opts.build.to_string_lossy();
 
     let path = path
         .canonicalize()
-        .wrap_err_with(|| eyre!("could not get absolute path: {}\n", path.to_string_lossy()))?;
+        .wrap_err_with(|| eyre!("could not get absolute path: {}\n", path.display()))?;
     let path = path.to_string_lossy();
     let path = path.replace(&*source, &*build);
     let mut path = PathBuf::from(path);
index 2146e6253ceb46af5fdbd5e5de8231584c42e1e3..a76ea7e09b1353fce9e64459f0f763753d7c2600 100644 (file)
@@ -1,5 +1,5 @@
-use color_eyre::{eyre::bail, Result};
-use metaforge::{build_metafile, parse_opts};
+use color_eyre::{eyre::eyre, Result};
+use metaforge::{build_metafile, log, parse_opts};
 use std::path::PathBuf;
 use walkdir::WalkDir;
 
@@ -8,23 +8,41 @@ fn main() -> Result<()> {
 
     let opts = parse_opts()?;
 
+    log!(opts, "finding files", 2);
     let files: Vec<PathBuf> = WalkDir::new(&opts.source)
         .into_iter()
-        .filter_map(|file| file.ok())
-        .filter(|file| file.file_type().is_file())
-        .map(|file| file.into_path())
+        .filter_map(|file| {
+            if file.as_ref().ok()?.file_type().is_dir() {
+                // need to create directories in build dir
+                let path = file.unwrap().into_path();
+                let path = path.strip_prefix(&opts.source).ok()?;
+                let path = opts.build.join(path);
+                log!(opts, format!("\tcreating dir: {}", path.display()), 3);
+                std::fs::create_dir(path).ok()?;
+                // don't need them for any further operations so we filter them out
+                None
+            } else {
+                if let Ok(file) = file {
+                    log!(opts, format!("\tadding file: {}", file.path().display()), 3);
+                    Some(file.into_path())
+                } else {
+                    None
+                }
+            }
+        })
         .collect();
 
+    log!(opts, "building files", 2);
     for file in files.iter() {
         match build_metafile(file, &opts) {
             Ok(_) => continue,
             Err(e) => {
                 if opts.force {
                     // print a line to stderr about failure but continue with other files
-                    eprintln!("error in {}: {}", file.to_string_lossy(), e);
+                    eprintln!("{}: {}", file.display(), e);
                     continue;
                 } else {
-                    bail!("error in {}: {}", file.to_string_lossy(), e);
+                    return Err(e.wrap_err(eyre!("{}:", file.display())));
                 }
             }
         }
index 5c22db609f8c027d97a7eb6fe3daaffd329b0169..18afa9c5a35ab8fa0ed70e3c9f9f262836d92784 100644 (file)
@@ -20,10 +20,11 @@ pub struct Opts {
     /// Pattern directory [CURRENT_DIR/pattern]
     #[arg(short, long, value_name = "PATTERN_DIR")]
     pattern: Option<String>,
-    /// Extra output [false]
-    #[arg(short, long, default_value_t = false)]
-    verbose: bool,
-    /// Minimal output [false]
+    /// Enable extra output.
+    /// Repeated flags give more info
+    #[arg(short, long, action = clap::ArgAction::Count)]
+    verbose: u8,
+    /// Minimal output
     #[arg(short, long, default_value_t = false)]
     quiet: bool,
     /// Don't stop if a single file fails [false]
@@ -40,7 +41,7 @@ pub struct Options {
     pub source: PathBuf,
     pub build: PathBuf,
     pub pattern: PathBuf,
-    pub verbose: bool,
+    pub verbose: u8,
     pub quiet: bool,
     pub force: bool,
     pub undefined: bool,
@@ -53,7 +54,7 @@ impl Options {
             source: PathBuf::new(),
             build: PathBuf::new(),
             pattern: PathBuf::new(),
-            verbose: false,
+            verbose: 0,
             quiet: false,
             force: false,
             undefined: false,
@@ -61,6 +62,15 @@ impl Options {
     }
 }
 
+#[macro_export]
+macro_rules! log {
+    ($opts:ident, $string:expr, $level:expr) => {
+        if $opts.verbose >= $level {
+            println!("{}", $string);
+        }
+    };
+}
+
 pub fn parse_opts() -> Result<Options> {
     let opts = Opts::parse();
 
index f2f7c228205be7e8ceae7699d2d1da348e2cadd2..d9a4be6515d096328a78c645cd9c8cc473787651 100644 (file)
@@ -69,7 +69,7 @@ fn parse_pattern_file() -> Result<()> {
     pattern_src.next();
     assert_eq!(pattern_src.next().unwrap(), source!(arr("array")));
     pattern_src.next();
-    assert_eq!(pattern_src.next().unwrap(), source!(pat("pattern")));
+    assert_eq!(pattern_src.next().unwrap(), source!(pat("test.blank")));
     pattern_src.next();
     assert_eq!(pattern_src.next(), None);
 
index 0aaa7cc32fe6bf45209d8a8cf4e12b64ef57aff3..7b542693de22c8951cc8c30cbbea7295bba54987 100644 (file)
@@ -3,5 +3,5 @@
 <ul>
     @{array}
 </ul
-&{pattern}
+&{test.blank}
 <footer>Other stuff</footer>
diff --git a/test_site/source/sub_dir/deep/deep.meta b/test_site/source/sub_dir/deep/deep.meta
new file mode 100644 (file)
index 0000000..cef3adb
--- /dev/null
@@ -0,0 +1,14 @@
+${
+    variable = 'GOOD'
+}
+
+@{
+    test.pattern.array = ["GOOD"]
+}
+
+&{
+    test = 'pattern'
+    test.blank = BLANK
+}
+
+&{test}
diff --git a/test_site/source/sub_dir/sub_dir.meta b/test_site/source/sub_dir/sub_dir.meta
new file mode 100644 (file)
index 0000000..0cbb769
--- /dev/null
@@ -0,0 +1,5 @@
+${
+    var = 'variable'
+}
+
+This is a simple filler file with a single variable: ${var}
index 4498a69ce50bcd48b2fc0a6aa1e9e1fa7e8a3058..e83e0c72b2293b22debe4cbb7b53911dae40eb49 100644 (file)
@@ -40,7 +40,7 @@ fn build_options() -> Result<Options> {
         source: dir.join("source"),
         build: dir.join("build"),
         pattern: dir.join("pattern"),
-        verbose: false,
+        verbose: 0,
         quiet: false,
         force: false,
         undefined: false,