From 092580eef8afa3ad35db0c28de0910153ded5544 Mon Sep 17 00:00:00 2001 From: British Werewolf Date: Wed, 27 Mar 2024 00:16:39 +0000 Subject: [PATCH] Update version. Add `directives` explanation to README. Fix that markdown directive wasn't being added to Vector. Abstract markdown mutation out of `create_variable` into the main method to handle mutating of any variable, not just content. Change parser to always add markdown if variable is named `content`. --- Cargo.toml | 2 +- README.md | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 39 ++++++++++++++++++++++++++--------- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3aa57bf..7848c13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "blogs-md-easy" description = "Iteratively convert a collection of Markdown files into a respective HTML template." -version = "0.1.5" +version = "0.2.0" edition = "2021" keywords = ["markdown", "html", "template", "blog"] categories = ["command-line-interface", "config", "web-programming"] diff --git a/README.md b/README.md index 1ca3553..c2c414c 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,62 @@ Variables can be reused as many times as required, and will be replaced, providi Finally, the `£content` variable is automatically generated based on the entire body of the Markdown file. +#### Directives +It's possible to mutate the placeholders during rendering by providing directives. +A directive is just a way of applying a pre-defined function to any placeholder variable. + +Let's use our previous template, and apply a simple directive to the `£title` variable. +```html + + + + + + + + {{ £title }} + + +
+

{{ £title | uppercase }}

+

Authored by {{ £author }}

+
+
{{ £content }}
+ + +``` +By providing the function after a pipe (`|`) character, we can mutate that variable in that particular location. This is particularly useful in cases where a placeholder is required multiple times through a template, but the formatting should be different in all cases. + +There are currently three supported directives: +* `lowercase` - Convert the value to lowercase. +* `uppercase` - Convert the value to uppercase. +* `markdown` - Convert the value from Markdown into HTML. + +By default, no directives will be provided, unless specified within the template, with the exception of `£content` which will have `markdown` applied. + +Directives are case insensitive, meaning `| uppercase` is the same as `| UPPERCASE`. They can also be chained together, such as in the following example. +```html + + + + + + + + {{ £title }} + + +
+ {{ £title | uppercase | markdown }} +

Authored by {{ £author }}

+
+
{{ £content }}
+ + +``` +Here we are converting the title to uppercase, and then mutating the value into markdown. +Chained directives are evaluated from left to right. + ### Markdowns [Markdowns](https://daringfireball.net/projects/markdown) are simple text files that contain any text, and an optional `meta` section. @@ -171,7 +227,8 @@ Currently, a new line is placed before all headings (from `h2` to to `h6`), but ## Todo List - [ ] Add if statements to render content based on a condition. -- [ ] Add directives to placeholders. +- [x] Add directives to placeholders. + - [ ] Add directives that support arguments. - [ ] Add tag filter to prevent parsing scripts. - [ ] Add better handling for errors in meta sections. - For example if a key is passed without a value, then no meta values are parsed. diff --git a/src/main.rs b/src/main.rs index 995ca47..9d64cb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -361,6 +361,7 @@ fn parse_placeholder_directive_enum(input: Span) -> IResult arg.and_then(|arg| Some(Directive::Date(arg.to_string()))), "lowercase" => Some(Directive::Lowercase), "uppercase" => Some(Directive::Uppercase), + "markdown" => Some(Directive::Markdown), _ => None, }; Ok((input, directive)) @@ -411,7 +412,12 @@ fn parse_placeholder(input: Span) -> IResult { many0(parse_placeholder_directive), tuple((multispace0, tag("}}"))), ))(input) - .map(|(input, (start, variable, directives, end))| { + .map(|(input, (start, variable, mut directives, end))| { + // By default, £content will always be parsed as Markdown. + if variable.to_ascii_lowercase().as_str() == "content" && !directives.contains(&Some(Directive::Markdown)) { + directives.push(Some(Directive::Markdown)); + } + (input, Placeholder { name: variable.to_string(), directives: directives.into_iter().filter_map(|d| d).collect(), @@ -535,14 +541,6 @@ fn create_variables(markdown: Span, meta_values: Vec>) -> Result Result<(), anyhow::Error> { // Get only existing markdowns. let markdown_urls: Vec = cli.markdowns .into_iter() - .filter(|file| file.exists() && file.extension().unwrap_or_default() == "md" ) + .filter(|file| file.exists() && file.extension().unwrap_or_default() == "md") .collect(); let markdowns: Vec = markdown_urls .iter() @@ -602,6 +600,27 @@ fn main() -> Result<(), anyhow::Error> { for placeholder in &placeholders { if let Some(variable) = variables.get(&placeholder.name) { + // Used to deref the variable. + let mut variable = variable.to_owned(); + + for directive in &placeholder.directives { + variable = match directive { + Directive::Lowercase => variable.to_lowercase(), + Directive::Uppercase => variable.to_uppercase(), + Directive::Markdown => { + markdown::to_html_with_options(&variable, &markdown::Options { + compile: markdown::CompileOptions { + allow_dangerous_html: true, + allow_dangerous_protocol: false, + ..Default::default() + }, + ..Default::default() + }).unwrap_or_default() + } + _ => unimplemented!(), + } + } + html_doc = replace_substring(&html_doc, placeholder.selection.start.offset, placeholder.selection.end.offset, &variable); } else { let url = markdown_url.to_str().unwrap_or_default();