diff --git a/README.md b/README.md index 5c1628b..a869f53 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ The [`handlebars-rust`](https://github.com/sunng87/handlebars-rust) project is u * Only files ending in `.hbs` are treated as templates. * All `.hbs` files in `PARTIALS_PATH` can be loaded in any Handlebars template using the filename without the `.hbs` extension. For example, `{PARTIALS_PATH}/layout.html.hbs` can be used with `{{#> layout.html}}` or similar. * All `.json` files in `DATA_PATH` are automatically loaded and made available under the `data` property using the filename without the `.json` extension. For example, `{DATA_PATH}/navbar.json` can be used with `{{#each data.navbar}}...{{/each}}` or similar. +* The `*set` decorator can be used to set properties on the context. A key and value must be provided For example, `{{*set "mykey" "a value"}}` will let you then call `{{mykey}}` later in the rendering. * The `*status` decorator can be used to set the status code used for the response. The value in the last call to the decorator will be the one used. The parameter must be one of the `Status` slugs in `src/response.rs`. For example, `{{*status "unauthenticated"}}` and `{{*status "other_server_error"}}` are valid calls. * The `*media-type` decorator can be used to set the response media type (i.e. `Content-Type` in HTTPS responses). For example, `{{*media-type "text/csv"}}` and `{{*media-type "application/json"}}` are valid calls. * The `*temporary-redirect` and `*permanent-redirect` decorators can be used to set temporary and permanent redirects respectively. For example, `{{*temporary-redirect "https://google.com/"}}` will return a temporary redirect to `https://google.com`. For consistency with Gemini, no response body will be returned with HTTPS responses when a redirect is made regardless of it's position in the template (templates will always render in full unless an error occurs). diff --git a/src/templates.rs b/src/templates.rs index 7f1caca..18398e5 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -41,6 +41,7 @@ pub fn initialize_handlebars(handlebars: &mut Handlebars) { Box::new(serialize_context_helper), ); handlebars.register_helper("pick-random", Box::new(pick_random_helper)); + handlebars.register_decorator("set", Box::new(set_decorator)); handlebars.register_decorator("temporary-redirect", Box::new(temporary_redirect_decorator)); handlebars.register_decorator("permanent-redirect", Box::new(permanent_redirect_decorator)); handlebars.register_decorator("status", Box::new(status_decorator)); @@ -288,3 +289,32 @@ fn permanent_redirect_decorator<'reg: 'rc, 'rc>( rc.set_context(new_ctx); Ok(()) } + +fn set_decorator<'reg: 'rc, 'rc>( + d: &Decorator, + _: &Handlebars, + ctx: &Context, + rc: &mut RenderContext, +) -> Result<(), RenderError> { + let key = match d.param(0) { + Some(param) => match param.value().as_str() { + Some(key_str) => Ok(key_str), + None => Err(RenderErrorReason::ParamNotFoundForIndex("set", 0)), + }, + None => Err(RenderErrorReason::ParamNotFoundForIndex("set", 0)), + }?; + + let value = d + .param(1) + .ok_or(RenderErrorReason::ParamNotFoundForIndex("set", 1))?; + + let mut new_ctx = ctx.clone(); + { + let data = new_ctx.data_mut(); + if let Some(ref mut m) = data.as_object_mut() { + m.insert(key.to_string(), to_json(value.value().render())); + } + } + rc.set_context(new_ctx); + Ok(()) +}