utkarshkukreti / markup.rs

A blazing fast, type-safe template engine for Rust.
Apache License 2.0
350 stars 14 forks source link

How to assign (already-declared) variables in a `@match` #41

Open rben01 opened 7 months ago

rben01 commented 7 months ago

I have a match that looks like this:

markup::define! {
    Test() {
        @let x = Some(1);
        @let y;
        @match x {
            Some(n) => { y = n },
            None => { y = -1 },
        }
    }
}

The error I get is

error: expected one of: `;`, curly braces, string literal
   --> src/lib.rs:127:19
    |
127 |             Some(n) => { y = n },
    |                            ^

The easiest fix is to simply write @let _ = match { ... }, as this “reenables” some Rust syntax that is seemingly not allowed within @match. But then this leads to a clippy warning, which I can't figure out how to silence because #[allow(clippy::let_unit_value)] isn't allowed in define!. (I could silence the warning at a coarser scope, but don't want to.)

I've tried various things, mostly mucking around with moving an @ sign here and there, and removing the let (so just _ = match { ... } and @_ = match { ... }, at least one of which feels like it should be legal, but no such luck). I can't figure out how to just assign a variable as a statement inside a @match. Various other solutions lead to the trait bound(): markup::Renderis not satisfied (i.e., an assignment itself cannot be rendered — fair enough).

Does markup support a way to accomplish this (maybe y @= n?), or to more generally include arbitrary rust code in define!? I'd also reached for ${ ... } as a way to say “the stuff in the {} is to be run but not rendered” but that didn't work either.

Edit: I've wrapped the whole match in a @fn, which gives me access to all the Rust syntax I need, but this seems like overkill.

fanelfaa commented 6 months ago

if you just want to set the @let y variable you can do like this:

markup::define! {
    Test() {
        @let x = Some(1);
        @let y = match x {
            Some(n) => n,
            None => -1,
        };
                @y
    }
}
rben01 commented 6 months ago

Thanks, but my issue is that I'd like to assign multiple variables. My plan is to pre declare the variables and then assign in the match. If I used a @let, I'd need to use a tuple to do the assignment, which gets unwieldy (especially when the assignment logic is determined at different levels of nested matches).

Kijewski commented 6 months ago

I'd propose to simply remove @let variable; patterns. You can always refactor your match statements to return a tuple, so that all variables are assigned afterwards.

rben01 commented 6 months ago

My issue is that I would like to replicate the following:

let x = Some(1_u32);
let y;
let z;

match x {
    Some(n) => {
        y = "some";
        z = match n {
            0 => "zero",
            1..=5 => "small",
            6..=10 => "medium",
            11.. => "big"
        };
    }
    None => {
        y = "none";
        z = "n/a"
    }
}

Were I to convert to using tuples, I would have to include "some" in every branch of z's match in the Some(n) branch, which is a bit annoying (and unnecessary in standard Rust).