Skip to content

Commit

Permalink
✨ Adding a replace_section helper (#60)
Browse files Browse the repository at this point in the history
* ✨ Adding a replace_section helper

* 🎨 Add a remove_boundaries parameter
  • Loading branch information
IgorLaborieWefox committed Aug 22, 2023
1 parent 256235e commit 75ba16e
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ toml = { version = "^0.7", optional = true, features = ["preserve_order"] }
spectral = "0.6"
tempfile = "3.1"
pretty_assertions = "1.1"
similar-asserts = "1.4"
unindent = "0.2"

[features]
Expand Down
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,53 @@ Helper able to assign (to set) a variable to use later in the template.
| `{{ assign "foo" "hello world" }}{{ foo }}` | `hello world` |
| `{{ assign "foo" {} }}{{ foo }}` | `[object]` |
| `{{ assign "foo" {"bar": 33} }}{{ foo }}` | `[object]` |

## Replace section

Helper able to replace a section delimited by a boundary.

For example with this template:

```handlebars
{{~#replace_section begin="<!-- #region head-->" end="<!-- #endregion head -->" content }}
This is the new content of the block
{{~/replace_section}}
```

And the `content` having

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- #region head-->
Something by default
<!-- #endregion head -->
</body>
</html>
```

The content between `<!-- #region head-->` and `<!-- #endregion head -->` is replaced by the result of the inner template:

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- #region head-->This is the new content of the block<!-- #endregion head -->
</body>
</html>
```

Note: you can remove the boundary by adding `remove_boundaries=true`.
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub mod outputs;
pub mod path_helpers;
#[cfg(feature = "string")]
pub mod string_helpers;
pub mod region_helpers;

#[derive(Debug, Error)]
enum HelperError {
Expand Down Expand Up @@ -60,6 +61,7 @@ pub fn register(handlebars: &mut Handlebars) {
jsonnet_helpers::register(handlebars);
assign_helpers::register(handlebars);
file_helpers::register(handlebars);
region_helpers::register(handlebars);
}

#[cfg(test)]
Expand Down
169 changes: 169 additions & 0 deletions src/region_helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use handlebars::{
Context, Handlebars, Helper, HelperDef, HelperResult, Output, RenderContext, Renderable,
};
use log::warn;

pub fn register(handlebars: &mut Handlebars) {
handlebars.register_helper("replace_section", Box::new(ReplaceSectionHelper))
}

#[derive(Clone, Copy)]
pub struct ReplaceSectionHelper;

impl HelperDef for ReplaceSectionHelper {
fn call<'reg: 'rc, 'rc>(
&self,
h: &Helper<'reg, 'rc>,
hbs: &'reg Handlebars<'reg>,
ctx: &'rc Context,
rc: &mut RenderContext<'reg, 'rc>,
out: &mut dyn Output,
) -> HelperResult {
let Some(tmpl) = h.template() else {
warn!("`replace_section` helper require a template");
return Ok(());
};

let Some(input) = h.param(0).and_then(|it| it.value().as_str()) else {
warn!("`replace_section` helper require an string parameter");
return Ok(());
};

let remove_boundaries = h
.hash_get("remove_boundaries")
.and_then(|it| it.value().as_bool())
.unwrap_or_default();

let Some(begin) = h.hash_get("begin").and_then(|it| it.value().as_str()) else {
warn!("`replace_section` helper require a 'begin' string value");
return Ok(());
};
let Some((before, inner)) = input.split_once(begin) else {
warn!("Begin region '{begin}' not found in '{input}'");
return Ok(())
};

let Some(end) = h.hash_get("end").and_then(|it| it.value().as_str()) else {
warn!("`replace_section` helper require a 'end' string value ");
return Ok(());
};
let Some((_, after)) = inner.split_once(end) else {
warn!("End region '{end}' not found in '{inner}'");
return Ok(())
};

out.write(before)?;
if !remove_boundaries {
out.write(begin)?;
}
tmpl.render(hbs, ctx, rc, out)?;
if !remove_boundaries {
out.write(end)?;
}
out.write(after)?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use std::error::Error;

use serde_json::json;
use similar_asserts::assert_eq;

use crate::new_hbs;

const INPUT: &str = r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- #region head-->
Something by default
<!-- #endregion head -->
</body>
</html>
"#;

#[test]
fn test_helper_replace_section() -> Result<(), Box<dyn Error>> {
let data = json!({
"content": INPUT,
});
let mut hbs = new_hbs();
hbs.register_template_string(
"test",
r#"{{#replace_section begin="<!-- #region head-->" end="<!-- #endregion head -->" content }}
This is the new content of the block
{{/replace_section}}"#,
)?;

let result = hbs.render("test", &data)?;

assert_eq!(
result,
r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- #region head-->
This is the new content of the block
<!-- #endregion head -->
</body>
</html>
"#,
);

Ok(())
}

#[test]
fn test_helper_replace_section_remove_remove_boundaries() -> Result<(), Box<dyn Error>> {
let data = json!({
"content": INPUT,
});
let mut hbs = new_hbs();
hbs.register_template_string(
"test",
r#"{{~#replace_section begin="<!-- #region head-->" end="<!-- #endregion head -->" remove_boundaries=true content }}
This is the new content of the block
{{~/replace_section}}"#,
)?;

let result = hbs.render("test", &data)?;

assert_eq!(
result,
r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
This is the new content of the block
</body>
</html>
"#,
);

Ok(())
}
}

0 comments on commit 75ba16e

Please sign in to comment.