/target
files/test_site/build
-files/README/build
+docs/build
files/bench_site/build
bacon.toml
# It is not intended for manual editing.
version = 3
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "anes"
version = "0.1.6"
[[package]]
name = "clap"
-version = "4.3.0"
+version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
+checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28"
dependencies = [
"clap_builder",
"clap_derive",
[[package]]
name = "clap_builder"
-version = "4.3.0"
+version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
+checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980"
dependencies = [
"anstream",
"anstyle",
[[package]]
name = "clap_derive"
-version = "4.3.0"
+version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b"
+checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b"
dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.18",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
[[package]]
name = "cpufeatures"
version = "0.2.7"
"typenum",
]
+[[package]]
+name = "css-minify"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "874c6e2d19f8d4a285083b11a3241bfbe01ac3ed85f26e1e6b34888d960552bd"
+dependencies = [
+ "derive_more",
+ "indexmap",
+ "nom",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 1.0.109",
+]
+
[[package]]
name = "digest"
version = "0.10.7"
[[package]]
name = "log"
-version = "0.4.17"
+version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
[[package]]
name = "metaforge"
-version = "0.1.1"
+version = "0.1.3"
dependencies = [
- "clap 4.3.0",
+ "clap 4.3.1",
"criterion",
"eyre",
+ "minify-html",
"pandoc",
"pest",
"pest_derive",
"thiserror",
]
+[[package]]
+name = "minify-html"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4d9147754a49e80557df835eb59e743eab1bf75410a134f55dc4b9dbb692ad"
+dependencies = [
+ "aho-corasick",
+ "css-minify",
+ "lazy_static",
+ "memchr",
+ "minify-js",
+ "rustc-hash",
+]
+
+[[package]]
+name = "minify-js"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c300f90ba1138b5c5daf5d9441dc9bdc67b808aac22cf638362a2647bc213be4"
+dependencies = [
+ "lazy_static",
+ "parse-js",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
[[package]]
name = "num-traits"
version = "0.2.15"
[[package]]
name = "once_cell"
-version = "1.17.1"
+version = "1.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
[[package]]
name = "oorandom"
"itertools 0.8.2",
]
+[[package]]
+name = "parse-js"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30534759e6ad87aa144c396544747e1c25b1020bd133356fd758c8facec764e5"
+dependencies = [
+ "aho-corasick",
+ "lazy_static",
+ "memchr",
+]
+
[[package]]
name = "pest"
version = "2.6.0"
"pest_meta",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.18",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
[[package]]
name = "rustix"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+[[package]]
+name = "semver"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+
[[package]]
name = "serde"
version = "1.0.163"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.18",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
[[package]]
name = "syn"
version = "2.0.18"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.18",
]
[[package]]
"once_cell",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.18",
"wasm-bindgen-shared",
]
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.18",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[package]
name = "metaforge"
-version = "0.1.2"
+version = "0.1.3"
edition = "2021"
[dependencies]
pest = "2"
pest_derive = "2"
rayon = "1.7"
+minify-html = "0.11"
[dev-dependencies]
criterion = "0.4"
-# metaforge - v0.1.2
+# metaforge - v0.1.3
a pattern driven static site generator for extensible snippet insertion.
## about
metaforge is a static site generator that lets you write something once, and re-use it
-across your site. it requires previous knowledge of html, and doesn't come with pre-made
-themes or templates except for a completely bare skeleton directory. it gives you extremely
-fine grained control over the generated html, customizing each pattern to a source file
+across your site.
+
+metaforge doesn't come with pre-made themes or templates except for a completely bare skeleton directory.
+it gives you extremely fine grained control over the generated html, customizing each pattern to a source file
with variables and mappable arrays.
metaforge also lets you write metapatterns that contain classes, masking choices to a
such as an rss feed. metaforge can also technically translate between any two document formats pandoc supports,
but nothing other than the default markdown to html gets tested.
-the full documentation is available in this repository under the **files/README/source/docs**
+the full documentation is available in this repository under the **docs/source**
directory. it's currently setup as a working example of a metaforge site, so you can poke around
and see one way a site can be setup.
this command can be run as many times as needed to regenerate the documentation, and is
reccomended after upgrading to a new version to see what's changed. the generated docs will
-be available in **files/README/build**, and can be looked at in any web browser.
+be available in **docs/build**, and can be looked at in any web browser.
opts.clean = true;
opts.parallel = true;
- c.bench_function("build dir", |b| {
+ c.bench_function("parallel build", |b| {
if opts.build.exists() {
std::fs::remove_dir_all(&opts.build).expect("clean build dir");
}
${
author = 'huck boles'
- version = '0.1.2'
+ version = '0.1.3'
home = './index.html'
}
-{ index is a folder up in this directory }
home = '../index.html'
-{ using substitutions for example code so it doesn't mess up parsing }
- var = '${ '
- arr = '@{ '
- pat = '&{ '
- head = '#{ '
- com = '-{ '
+ var = '${'
+ arr = '@{'
+ pat = '&{'
+ head = '#{'
+ com = '-{'
}
${
title = 'definitions'
description = 'definining variables and patterns'
- arr_sub = '@{'
}
## rules
foo.bar = [ 'foobar', 'foobaz' ]
${com} this will copy pattern/foo/*.meta twice, inserting 'foobar and 'foobaz'
- once each at the location of ${arr_sub}bar} }
+ once each at the location of ${arr}bar} }
}
${com} all of these patterns are only defined for this file }
${
title = 'expansions'
description = 'expanding variables and patterns in a file'
- var_sub = '${'
- arr_sub = '@{'
- pat_sub = '&{'
}
## syntax
### examples
- ...this is a string with a ${var_sub}variable} to be expanded...
+ ...this is a string with a ${var}variable} to be expanded...
- ...this line has a ${pat_sub}pattern} inside of it...
+ ...this line has a ${pat}pattern} inside of it...
- ...this ${arr_sub}array} will be replaced...
+ ...this ${arr}array} will be replaced...
## behavior
quux = BLANK
}
- pattern [foo]: <p>${var_sub}baz} ${var}quux}</p>
+ pattern [foo]: <p>${var}baz} ${var}quux}</p>
expanded [foo]: <p>foo </p>
- pattern [bar]: <p>${var_sub}baz} ${var}quux}</p>
+ pattern [bar]: <p>${var}baz} ${var}quux}</p>
expanded [bar]: <p>quux </p>
#### example
- pattern [foo]: <p>${arr_sub}bar}</p>
+ pattern [foo]: <p>${arr}bar}</p>
defintion: ${arr} foo.bar = ['foo', 'bar', 'baz'] }
goes through an extra step right before insertion, calling pandoc on the file
to convert between the chosen filetypes.
+***SOURCE*** can also be used as a standin for the source directory while writing expansions,
+allowing patterns to call the same source file every time, or source files to expand other
+source files.
+
+### example
+ ...lorem ${pat}SOURCE.foo.bar} ipsum dolor...
+
once the filename is determined, it is parsed and expands any contained variables,
arrays and patterns. if it is a ***SOURCE*** pattern, it is converted to html after
the expansions. the expanded pattern is then inserted in place of the calling identifier.
## building
-as each file is built, the first thing expanded is the relevant **base/[FILE].meta** pattern,
-so it is required to have at least a **default.meta** in the **pattern/base** directory
+as each source file is built, the first thing expanded is the defined or default
+**[PATTERN]/base/[FILE].meta** pattern, so it is required to have at least a **default.meta**
+in the **pattern/base** directory
### example
- pattern [base]: <html>${pat_sub}body}</html>
+ pattern [base]: <html>${pat}body}</html>
- pattern [body]: <body>${pat_sub}SOURCE}</body>
+ pattern [body]: <body>${pat}SOURCE}</body>
source [SOURCE]: foo *bar* baz
--undefined
panics and stops building site if any undefined variables are encountered
--no-pandoc
- don't call pandoc on source files
- allows metaforge to run without pandoc installed
+ don't call pandoc on source files. allows metaforge to run without pandoc installed
+ --no-minify
+ don't minify resulting html
- blank = **BOOL** - if true, stops parsing and returns an empty string
- panic_default = **BOOL** - if true, panics on an undefined default pattern
- panic_undefined = **BOOL** - if true, panics on an undefined variable or array
+- source = **STRING** - change the the filetype of the source file
+- filetype = **STRING** - change the filetype of the output file
- equal_arrays = **BOOL** - if true, panics if arrays in the same pattern have different sizes
+- minify = **BOOL** - toggles html minification
+- pandoc = **BOOL** - toggles if pandoc is ran on this file to convert between filetypes, defaults to *true* in **source** dir, and *false* in **pattern** dir.
### source
- ignore = **BOOL** - stops parsing and skips this file, useful for ignoring directories with scoped definitions
-- source = **STRING** - change the the filetype of the source file
-- filetype = **STRING** - change the filetype of the output file
-- pandoc = **BOOL** - toggles if pandoc is ran on this file to convert between filetypes
+- copy_only = **BOOL** - copys file or directory without processing anything
but can contain anything that you'd like to substitute.
required directories are:
+
- source (site structure and contents)
- pattern (patterns for expansion)
- pattern/base (gets expanded to start building each pattern)
foobaz }
## layout
-- optional header definition block
-- optional variable, array, pattern definition blocks
+
+*all sections are optional*
+
+- header definition block
+- variable, array, pattern definition blocks
- source
### example
currently it's unstyled html rendered by your browser, so it's pretty bare-bones.
-open **files/README** in metaforge's repository and explore the source and pattern
+open **docs/source** in metaforge's repository and explore the source and pattern
files to get some examples of how this site works.
-you can change anything in the **README** directory and see how it affects the site once
+you can change anything in the **docs** directory and see how it affects the site once
you rebuild it.
## contents
+
- [syntax](docs/syntax.html)
- [definitions](docs/definitions.html)
- [expansions](docs/expansions.html)
- [headers](docs/header.html)
- [flags](docs/flags.html)
+## new in this version
+
+- spaces are correctly preserved between directly adjacent substitutions
+- include from source dir
+- copy_only header directive
+- pandoc header now works on pattern files
+- html minification header and flags
+
## versions
+- 0.1.3: grammar and headers
- 0.1.2: multithreading
- 0.1.1: initial release
--- /dev/null
+&{SOURCE.unit_tests.expand.source_expand}
--- /dev/null
+#{ pandoc = true }
+
+# GOOD
--- /dev/null
+${ var = 'GOOD' }
+
+&{ test = 'expand_source' }
--- /dev/null
+GOOD
+${var}
--- /dev/null
+${
+ var1 = 'GOOD'
+ var2 = 'GOOD'
+}
+
+${var1} ${var2}
--- /dev/null
+#{ copy_only = true }
+
+variable: ${this} should get copied verbatim
--- /dev/null
+&{test = 'pandoc'}
+
+source
})),
}?;
- let file = parse_string(source, opts)?;
+ let mut file = parse_string(source, opts)?;
Ok(file.construct()?)
}
#[cfg(test)]
mod tests;
+use std::fmt::Display;
+
#[derive(Debug, Clone, PartialEq)]
pub enum Src {
Str(String),
}
impl Src {
- pub fn to_var(var: impl ToString) -> Self {
+ pub fn to_var(var: impl Display) -> Self {
Src::Var(var.to_string())
}
- pub fn to_arr(arr: impl ToString) -> Self {
+ pub fn to_arr(arr: impl Display) -> Self {
Src::Arr(arr.to_string())
}
- pub fn to_pat(pat: impl ToString) -> Self {
+ pub fn to_pat(pat: impl Display) -> Self {
Src::Pat(pat.to_string())
}
- pub fn to_str(str: impl ToString) -> Self {
+ pub fn to_str(str: impl Display) -> Self {
Src::Str(str.to_string())
}
}
-impl ToString for Src {
- fn to_string(&self) -> String {
- match self {
+impl Display for Src {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let str = match self {
Src::Var(x) | Src::Arr(x) | Src::Pat(x) | Src::Str(x) => x.to_string(),
- }
+ };
+
+ write!(f, "{str}")
}
}
use crate::{error::*, Options};
use eyre::Result;
+use minify_html::{minify, Cfg};
use std::{fs, path::PathBuf};
use super::*;
+const HTML_CFG: Cfg = Cfg {
+ do_not_minify_doctype: false,
+ ensure_spec_compliant_unquoted_attribute_values: false,
+ keep_closing_tags: true,
+ keep_html_and_head_opening_tags: true,
+ keep_spaces_between_attributes: false,
+ keep_comments: false,
+ minify_css: true,
+ minify_css_level_1: false,
+ minify_css_level_2: true,
+ minify_css_level_3: false,
+ minify_js: true,
+ remove_bangs: true,
+ remove_processing_instructions: true,
+};
+
impl<'a> DirNode<'a> {
pub fn build(path: PathBuf, opts: &'a Options) -> Result<Self> {
assert!(path.is_dir() && path.exists());
for f in fs::read_dir(&self.path)? {
let file = f?.path();
+ if self.global.header.copy_only {
+ let dest = self.global.dest()?;
+ fs::copy(file, &dest.parent().unwrap_or(&self.opts.build))?;
+ continue;
+ }
+
if file.is_dir() {
let dir = DirNode::build(file, self.opts)?;
self.dirs.push(dir);
file.merge(&self.global);
match file.construct() {
Ok(str) => {
- fs::write(file.dest()?, str)?;
+ if file.header.minify && self.opts.minify {
+ fs::write(file.dest()?, minify(str.as_bytes(), &HTML_CFG))?;
+ } else {
+ fs::write(file.dest()?, str)?;
+ }
}
Err(e) => {
// print a line to stderr about failure but continue with other files
use std::{collections::HashMap, path::PathBuf};
use super::*;
+
#[derive(Debug, Clone)]
pub struct MetaFile<'a> {
pub opts: &'a Options,
Ok(metafile)
}
- pub fn construct(&self) -> Result<String, Box<MetaError>> {
+ pub fn construct(&mut self) -> Result<String, Box<MetaError>> {
log!(self.opts, format!("building {}", self.path.display()), 1);
if self.header.blank {
return Err(Box::new(MetaError::Ignored));
}
- let html = self.to_html().map_err(MetaError::from)?;
+ if self.header.copy_only {
+ let dest = self.dest().map_err(MetaError::from)?;
+ let source: String = self.source.iter().map(|s| s.to_string()).collect();
+ std::fs::write(dest, source).unwrap();
+ return Err(Box::new(MetaError::Ignored));
+ }
+
+ let src_str: String;
+ if self.header.pandoc.map_or(true, |x| x) {
+ src_str = self.pandoc().map_err(MetaError::from)?;
+ } else {
+ src_str = self.get_source().map_err(MetaError::from)?;
+ }
let pattern = self.get_pattern("base").map_err(MetaError::from)?;
let mut base = parse_string(pattern, self.opts).map_err(|e| MetaError::ParserError {
})?;
base.merge(self);
- base.patterns.insert(Scope::into_global("SOURCE"), html);
+ base.patterns
+ .insert(Scope::create_global("SOURCE"), src_str);
+ let mut base_path = self.opts.pattern.join("base").join(
+ self.patterns
+ .get(&Scope::create_global("base"))
+ .unwrap_or(&"default".into()),
+ );
+
+ base_path.set_extension("meta");
+ base.path = base_path;
let output = base.get_source().map_err(MetaError::from)?;
let value = if let Some(val) = self.arrays.get(&name_key) {
&val[..]
- } else if let Some(val) = self.arrays.get(&name_key.to_global()) {
+ } else if let Some(val) = self.arrays.get(&name_key.global()) {
&val[..]
} else if let Some(val) = self.arrays.get(&class_key) {
&val[..]
- } else if let Some(val) = self.arrays.get(&class_key.to_global()) {
+ } else if let Some(val) = self.arrays.get(&class_key.global()) {
&val[..]
- } else if let Some(val) = self.arrays.get(&Scope::into_global(key)) {
+ } else if let Some(val) = self.arrays.get(&Scope::create_global(key)) {
&val[..]
- } else if let Some(val) = self.arrays.get(&Scope::into_local(key)) {
+ } else if let Some(val) = self.arrays.get(&Scope::create_local(key)) {
&val[..]
} else if self.opts.undefined {
panic!(
log!(self.opts, format!("expanding {key}"), 2);
// SOURCE is already expanded in the initial construct() call
if key == "SOURCE" {
- if let Some(source) = self.patterns.get(&Scope::into_global("SOURCE")) {
+ if let Some(source) = self.patterns.get(&Scope::create_global("SOURCE")) {
return Ok(source.to_string());
} else {
return Ok(String::new());
}
}
- let mut filename = if let Some(name) = self.patterns.get(&Scope::into_local(key)) {
+ let is_source = key.split('.').next().unwrap_or("") == "SOURCE";
+
+ let mut filename = if let Some(name) = self.patterns.get(&Scope::create_local(key)) {
Ok(name.to_string())
- } else if let Some(name) = self.patterns.get(&Scope::into_global(key)) {
+ } else if let Some(name) = self.patterns.get(&Scope::create_global(key)) {
Ok(name.to_string())
} else if self
.opts
.pattern
.join(key.replace(".", "/") + ".meta")
.exists()
+ || is_source
{
Ok(String::new())
} else if self.header.panic_default {
}
let pattern_path = key.replace('.', "/") + "/" + &filename;
- let mut path = self.opts.pattern.join(pattern_path);
- path.set_extension("meta");
+ let mut path = if is_source {
+ let pattern_path = pattern_path.replace("SOURCE/", "");
+ self.opts.source.join(pattern_path)
+ } else {
+ self.opts.pattern.join(pattern_path)
+ };
+
+ path.set_extension("meta");
let mut pattern = MetaFile::build(path, self.opts)?;
// copy over maps for expanding contained variables
pattern.merge(self);
- pattern.get_source()
+ if pattern.header.pandoc.unwrap_or(false) || is_source {
+ pattern.pandoc()
+ } else {
+ pattern.get_source()
+ }
}
}
use super::*;
impl<'a> MetaFile<'a> {
- pub fn to_html(&self) -> Result<String> {
+ pub fn pandoc(&mut self) -> Result<String> {
let string = self.get_source()?;
- if self.opts.no_pandoc || !self.header.pandoc || string.is_empty() {
+ if self.opts.no_pandoc || string.is_empty() {
return Ok(string);
}
.set_output_format(output, vec![]);
if let pandoc::PandocOutput::ToBuffer(s) = pandoc.execute()? {
+ self.header.pandoc = Some(false);
Ok(s)
} else {
Err(MetaError::Pandoc { file: self.name()? }.into())
2
);
let long_key = self.name()? + "." + &key.to_string();
- if let Some(val) = self.variables.get(&Scope::into_local(&long_key)) {
+ if let Some(val) = self.variables.get(&Scope::create_local(&long_key)) {
Ok(val.clone())
- } else if let Some(val) = self.variables.get(&Scope::into_global(&long_key)) {
+ } else if let Some(val) = self.variables.get(&Scope::create_global(&long_key)) {
Ok(val.clone())
- } else if let Some(val) = self.variables.get(&Scope::into_local(key)) {
+ } else if let Some(val) = self.variables.get(&Scope::create_local(key)) {
Ok(val.clone())
- } else if let Some(val) = self.variables.get(&Scope::into_global(key)) {
+ } else if let Some(val) = self.variables.get(&Scope::create_global(key)) {
Ok(val.clone())
} else if self.opts.undefined || self.header.panic_undefined {
return Err(MetaError::UndefinedExpand {
pub equal_arrays: bool,
pub filetype: String,
pub source: String,
- pub pandoc: bool,
+ pub pandoc: Option<bool>,
pub ignore: bool,
+ pub copy_only: bool,
+ pub minify: bool,
}
impl Header {
equal_arrays: false,
filetype: String::from("html"),
source: String::from("markdown"),
- pandoc: true,
+ pandoc: None,
ignore: false,
+ copy_only: false,
+ minify: true,
}
}
}
"panic_default" => header.panic_default = val == "true",
"panic_undefined" => header.panic_undefined = val == "true",
"equal_arrays" => header.equal_arrays = val == "true",
- "pandoc" => header.pandoc = val == "true",
+ "pandoc" => header.pandoc = Some(val == "true"),
"filetype" => header.filetype = val.to_string(),
"source" => header.source = val.to_string(),
"ignore" => header.ignore = val == "true",
+ "copy_only" => header.copy_only = val == "true",
+ "minify" => header.copy_only = val == "true",
_ => continue,
}
}
+use std::fmt::Display;
+
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub enum Scope {
Local(String),
}
impl Scope {
- pub fn into_local(str: impl ToString) -> Scope {
+ pub fn create_local(str: impl Display) -> Scope {
Scope::Local(str.to_string())
}
- pub fn into_global(str: impl ToString) -> Scope {
+ pub fn create_global(str: impl ToString) -> Scope {
Scope::Global(str.to_string())
}
}
}
- pub fn to_local(&self) -> Scope {
+ pub fn local(&self) -> Scope {
Scope::Local(self.to_string())
}
- pub fn to_global(&self) -> Scope {
+ pub fn global(&self) -> Scope {
Scope::Global(self.to_string())
}
}
-impl ToString for Scope {
- fn to_string(&self) -> String {
- match self {
+impl Display for Scope {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let str = match self {
Scope::Local(x) | Scope::Global(x) => x.to_string(),
- }
+ };
+
+ write!(f, "{str}")
}
}
#[derive(Parser, Debug)]
#[command(author = "huck boles")]
-#[command(version = "0.1.2")]
+#[command(version = "0.1.3")]
#[command(about = "customizable template driven static site generator")]
#[command(long_about = None)]
pub struct Opts {
/// don't call pandoc on source files
#[arg(long, default_value_t = false)]
pub no_pandoc: bool,
+ /// don't minify resulting html
+ #[arg(long, default_value_t = false)]
+ pub no_minify: bool,
}
#[derive(Debug, Clone, Default)]
pub clean: bool,
pub no_pandoc: bool,
pub new: bool,
+ pub minify: bool,
}
impl Options {
clean: false,
no_pandoc: false,
new: false,
+ minify: true,
}
}
}
opts.no_pandoc = value.no_pandoc;
opts.new = value.new;
opts.parallel = value.parallel;
+ opts.minify = !value.no_minify;
opts.root = if let Some(root) = value.root.as_deref() {
PathBuf::from(root).canonicalize()
#[cfg(test)]
mod tests;
-use crate::{log, Header, MetaError, MetaFile, Options};
+use crate::{Header, MetaError, MetaFile, Options};
use eyre::Result;
use pest::{
iterators::{Pair, Pairs},
pub struct MetaParser;
pub fn parse_string(file: String, opts: &Options) -> Result<MetaFile> {
- log!(opts, "parsing file", 3);
-
let pair = MetaParser::parse(Rule::file, &file)?.next().unwrap();
let mut meta_file = MetaFile::new(opts);
}
if global {
- Ok((Scope::into_global(key), val))
+ Ok((Scope::create_global(key), val))
} else {
- Ok((Scope::into_local(key), val))
+ Ok((Scope::create_local(key), val))
}
}
}
if global {
- Ok((Scope::into_global(key), val))
+ Ok((Scope::create_global(key), val))
} else {
- Ok((Scope::into_local(key), val))
+ Ok((Scope::create_local(key), val))
}
}
array = _{
"[" ~ "]"
- | "["
- ~ WHITESPACE*
- ~ string
- ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ string)*
+ | "["
+ ~ WHITESPACE*
+ ~ string
+ ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ string)*
~ WHITESPACE* ~ ","? ~ WHITESPACE*
~ "]"
}
var_sub = { &("$") ~ substitution }
arr_sub = { &("@") ~ substitution }
pat_sub = { &("&") ~ substitution }
-identifier = _{ var_sub | pat_sub | arr_sub }
+identifier = _{ var_sub | pat_sub | arr_sub | COMMENT}
-source = { (identifier | char_seq)* }
+source = ${ (identifier | char_seq)* }
file = {
SOI ~ header? ~ definition* ~ source? ~ EOI
-use crate::{MetaFile, Options};
+use crate::{MetaError, MetaFile, Options};
use eyre::Result;
use std::{fs, path::PathBuf};
let test_dir = opts.source.join("unit_tests");
let mut path = test_dir.join($file);
path.set_extension("meta");
- let file = MetaFile::build(path, &opts)?;
- assert_eq!(file.construct()?, $test);
+ let mut file = MetaFile::build(path, &opts)?;
+
+ let str = match file.construct() {
+ Ok(f) => f,
+ Err(e) => match *e {
+ MetaError::Ignored => return Ok(()),
+ e => return Err(e.into())
+ }
+
+ };
+
+ assert_eq!(str, $test);
Ok(())
}
};
let test_dir = opts.source.join("unit_tests");
let mut path = test_dir.join($file);
path.set_extension("meta");
- let file = MetaFile::build(path, &opts).unwrap();
+ let mut file = MetaFile::build(path, &opts).unwrap();
assert_eq!(file.construct().unwrap(), $test);
}
};
);
unit_test!(blank_pattern, "blank/blank_pattern", "");
-unit_test!(blank_variable, "blank/blank_variable", "<html>\n</html>\n");
-unit_test!(blank_array, "blank/blank_array", "<html>\n</html>\n");
-unit_test!(blank_comment, "blank/comment", "<html>\n</html>\n");
+unit_test!(
+ blank_variable,
+ "blank/blank_variable",
+ "<html>\n\n\n</html>\n"
+);
+unit_test!(blank_array, "blank/blank_array", "<html>\n\n\n</html>\n");
+unit_test!(blank_comment, "blank/comment", "<html>\n\n\n\n</html>\n");
unit_test!(
inline_comment,
"blank/inline_comment",
- "<html>\n<p>inline comment</p>\n</html>\n"
+ "<html>\n<p>inline comment</p>\n\n\n\n</html>\n"
);
unit_test!(
expand_var_in_src,
"expand/variable_in_source",
- "<html>\n<p>GOOD</p>\n</html>\n"
+ "<html>\n<p>GOOD</p>\n\n\n\n</html>\n"
);
unit_test!(
expand_var_in_pat,
"expand/variable_in_pattern",
- "<html>\nGOOD</html>\n"
+ "<html>\nGOOD\n\n\n</html>\n"
);
unit_test!(
expand_arr_in_src,
"expand/array_in_source",
- "<html>\n<p>12345</p>\n</html>\n"
+ "<html>\n<p>1 2 3 4 5</p>\n\n\n\n</html>\n"
);
unit_test!(
expand_arr_in_pat,
"expand/array_in_pattern",
- "<html>\n12345</html>\n"
+ "<html>\n1\n2\n3\n4\n5\n\n\n</html>\n"
);
unit_test!(
expand_pat_in_src,
"expand/pattern_in_source",
- "<p>GOOD</p>\n"
+ "<p>GOOD</p>\n\n"
);
unit_test!(
expand_pat_in_pat,
"expand/pattern_in_pattern",
- "<html>\nGOOD\nGOOD\n</html>\n"
+ "<html>\nGOOD\nGOOD\n\n\n\n</html>\n"
);
unit_test!(
override_var,
"override/variable",
- "<html>\n<p>GOOD</p>\n</html>\n"
+ "<html>\n<p>GOOD</p>\n\n\n\n</html>\n"
);
unit_test!(
override_pat,
"override/pattern",
- "<html>\nGOOD\nGOOD\n</html>\n"
+ "<html>\nGOOD\n GOOD\n\n\n\n</html>\n"
);
unit_test!(
header_no_pandoc,
"header/pandoc",
- "# This should not become html\n"
+ "# This should not become html\n\n"
);
unit_test!(header_blank, "header/blank", "");
unit_test!(
pat_file,
"expand/file.meta",
- "<html>\n<p>GOOD</p>\n</html>\n"
+ "<html>\n<p>GOOD</p>\n\n\n\n</html>\n"
);
unit_test!(
direct_call,
"expand/direct_call",
- "<html>\n<p>abcd</p>\n</html>\n"
+ "<html>\n<p>a b c d</p>\n\n\n\n</html>\n"
+);
+
+unit_test!(
+ expand_spaces,
+ "expand/spaces",
+ "<html>\n<p>GOOD GOOD</p>\n\n\n\n</html>\n"
+);
+
+unit_test!(
+ copy_header,
+ "header/copy",
+ r#"variable: ${this} should get copied verbatim"#
+);
+
+unit_test!(
+ expandoc,
+ "header/expandoc",
+ "<html>\n<h1 id=\"good\">GOOD</h1>\n\n\n</html>\n"
+);
+
+unit_test!(
+ include_source,
+ "expand/source",
+ "<html>\n<p>GOOD GOOD</p>\n\n\n\n</html>\n"
);
panic_test!(ignore, "ignore.meta", "");
assert_eq!(
fs::read_to_string(dir.join("build/unit_tests/global/pattern.html"))?,
- "<p>GOOD GOOD</p>\n"
+ "<p>GOOD</p><p>GOOD</p>"
);
assert_eq!(
fs::read_to_string(dir.join("build/unit_tests/global/variable.html"))?,
- "<p>GOODGOOD</p>\n"
+ "<p>GOOD GOOD</p>"
);
Ok(())
#[test]
#[ignore = "generates README site"]
fn readme() -> Result<()> {
- let dir = std::path::PathBuf::from("files/README")
+ let dir = std::path::PathBuf::from("docs")
.canonicalize()
.unwrap();