cargo-generate / cargo-generate

cargo, make me a project
https://cargo-generate.github.io/cargo-generate
Apache License 2.0
1.94k stars 161 forks source link

[docs] Explain which conditional blocks are valid in `cargo-generate.toml` #1119

Open Andrew15-5 opened 8 months ago

Andrew15-5 commented 8 months ago

Here is an example from https://github.com/dioxuslabs/dioxus-template (very verbose and repetitive):

original example ```toml [placeholders.platform] type = "string" prompt = "What platform are you targeting?" choices = ["web", "fullstack", "desktop", "liveview", "TUI"] default = "web" [conditional.'platform == "liveview"'.placeholders.backend] type = "string" prompt = "What backend framework are you using?" choices = ["Axum", "Warp", "Salvo"] default = "Axum" [conditional.'platform == "fullstack"'.placeholders.backend] type = "string" prompt = "What backend framework are you using?" choices = ["Axum", "Warp", "Salvo"] default = "Axum" [conditional.'platform == "web"'.placeholders.styling] type = "string" prompt = "How do you want to create CSS?" choices = ["Tailwind", "Vanilla"] default = "Vanilla" [conditional.'platform == "desktop"'.placeholders.styling] type = "string" prompt = "How do you want to create CSS?" choices = ["Tailwind", "Vanilla"] default = "Vanilla" [conditional.'platform == "fullstack"'.placeholders.styling] type = "string" prompt = "How do you want to create CSS?" choices = ["Tailwind", "Vanilla"] default = "Vanilla" ```

It uses the same conditional block as in https://cargo-generate.github.io/cargo-generate/templates/conditional.html.


Placeholder values defined in conditional sections cannot be used to enable/disable further conditional sections, they can however still be used in the actual template!

I don't understand this sentence. IIUC, the "placeholder values", i.e, .placeholders.<value> can be used to enable further conditional sections, because depending on which platform I chose the next questions will change depending on the value of the platform variable. So maybe this also should be addressed in the docs.


I tried using || operator in the conditions and surprisingly it worked! So I was able to optimize the config to this:

optimized example 1 ```toml [placeholders.platform] type = "string" prompt = "What platform are you targeting?" choices = ["web", "fullstack", "desktop", "liveview", "TUI"] default = "web" [conditional.'platform == "liveview" || platform == "fullstack"'.placeholders.backend] type = "string" prompt = "What backend framework are you using?" choices = ["Axum", "Warp", "Salvo"] default = "Axum" [conditional.'platform == "web" || platform == "desktop" || platform == "fullstack"'.placeholders.styling] type = "string" prompt = "How do you want to create CSS?" choices = ["Tailwind", "Vanilla"] default = "Vanilla" ```

I was going to ask if conditional is actually a Rust code evaluation, but after another successful optimization:

optimized example 2 ```toml [placeholders.platform] type = "string" prompt = "What platform are you targeting?" choices = ["web", "fullstack", "desktop", "liveview", "TUI"] default = "web" [conditional.'["liveview", "fullstack"].contains(platform)'.placeholders.backend] type = "string" prompt = "What backend framework are you using?" choices = ["Axum", "Warp", "Salvo"] default = "Axum" [conditional.'["web", "desktop", "fullstack"].contains(platform)'.placeholders.styling] type = "string" prompt = "How do you want to create CSS?" choices = ["Tailwind", "Vanilla"] default = "Vanilla" ```

I'm almost 100% sure that it is indeed a Rust conditional, or could be a JS? I wasn't able to use Some and unwrap like I can do here:

fn main() {
    if [Some("web").unwrap(), "other"].contains(&"web") {
        println!("Hello, world!");
    }
}
[conditional.'[Some("web").unwrap(), "desktop", "fullstack"].contains(platform)'.placeholders.styling]

(Also, TOML syntax highlighting breaks if I use [] inside the '<condition>'. yj successfully converts this to YAML and JSON, but fails at converting to TOML. So either yj isn't perfect or above syntax is indeed invalid. Does anyone know which one is true?)

So, maybe a restricted version of Rust?

Either way, I didn't find anything even close to "this level" of conditions in the docs, so I think the docs just missing it and should be updated with more examples and an explanation of what you can and can't do in conditional block.

Update: I tried using the optimized syntax and it does generate all the same stuff.

sassman commented 7 months ago

Hey @Andrew15-5,

that's a very good question! I've seen this stuff first some time ago in a Cargo.toml like

[target.'cfg(target_arch = "x86_64")'.dependencies]

and I found it quite creepy.

So I did not found much documentation about it on the toml repo. I added some more tests for now (see #1133) but I guess extended documentation and those examples of yours are valuable for users.

Andrew15-5 commented 7 months ago

Wait, are you saying cargo-generate is not the one responsible for evaluating the conditional blocks? Then who is?

sassman commented 7 months ago

Sorry, I found it this code here actually takes care of the conditional evaluation.

And, the whole logic is not backed by toml as I thought but by our rhai script hooks.

Haven't looked at this code in quite a while.

But I guess the whole conversation already reveals that our docs about the whole conditionals feature are not good enough.

Andrew15-5 commented 7 months ago

I never used Rhai. Is any valid Rhai conditional statement can be used in the conditional block? Or are there some restrictions?

sassman commented 7 months ago

it's a simple rhai expression that is evaluated.

Andrew15-5 commented 7 months ago

Ok. I'll probably check out Rhai in general to understand what kind of expressions can be evaluated. So that I can insert the most clever and ugly conditional statement in the cargo-generate.toml that has ever been created! /s