tera icon indicating copy to clipboard operation
tera copied to clipboard

Implement builder or macro for easier context creation

Open tp971 opened this issue 3 years ago • 3 comments

I think it would be nice to have a builder or a macro for creating a context in one expression like this:

//builder
let ctx = ContextBuilder::new()
    .with("...", ...)
    .with("...", ...);

//macro
let ctx = context! {
    ... => ...,
    ... => ...,
};

//current syntax
let mut ctx = Context::new();
ctx.insert("...", ...);
ctx.insert("...", ...);

tp971 avatar Nov 05 '22 22:11 tp971

I like the idea. Rocket does also provide a context macro for tera. I like the rocket syntax even more.

let context = context! {
        // Note that shorthand field syntax is supported.
        // This is equivalent to `foo: foo,`
        foo,
        bar: "Hello world",
    }
let context = context! {cargo_pkg_version: CARGO_PKG_VERSION, cargo_pkg_name: CARGO_PKG_NAME, style}

https://api.rocket.rs/master/rocket_dyn_templates/macro.context.html

This is much shorter and easier to read as the insert alternatives.

LuckyTurtleDev avatar Dec 30 '22 14:12 LuckyTurtleDev

I don't like the builder pattern but the macro approach could work.

Keats avatar Jan 02 '23 16:01 Keats

I have a macro that I developed for my own use that you can feel free to adopt. There's no need to bring serde into the mix like Rocket does, as Context::insert is even easlier to work with.

Happy to license it as needed for this project -- it's pretty trivial code. It allows four styles of context definition, which can be mixed and matched, and correctly handles extraneous trailing commas:

context! {
   id, /* Use value and type from variable in scope */
   id: type, /* Use value from variable in scope, force type resolution */
   id = expression, /* Use automatic type inference from expression */
   id: type = expression, /* Use forced type and expression */
}

https://github.com/mmastrac/progscrape/blob/87d0a877da4f6101e429e8028f8aa04cd85fc2a6/web/src/web.rs#L307


macro_rules! context_assign {
    ($id:ident , ,) => {};
    ($id:ident , , $typ:ty) => {
        let $id: $typ = $id;
    };
    ($id:ident , $expr:expr , $typ:ty) => {
        let $id: $typ = $expr;
    };
    ($id:ident , $expr:expr ,) => {
        let $id = $expr;
    };
}

macro_rules! context {
    ( $($id:ident $(: $typ:ty)? $(= $expr:expr)? ),* $(,)? ) => {
        {
            let mut context = Context::new();

            // Create a local variable for each item of the context, with a type if specified.
            $(
                context_assign!($id , $($expr)? , $($typ)?);
                context.insert(stringify!($id), &$id);
            )*

            context
        }
    };
}

mmastrac avatar Jan 26 '23 22:01 mmastrac