-
-
Notifications
You must be signed in to change notification settings - Fork 159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce features, a way to include files conditionally #230
Comments
You're 100% right - a feature like that can be appreciate, especially for big templates! I really like the idea behind the configuration file. |
For reference, I finally had some time to finish up old projects of mine and tinkered around with this idea for https://github.com/SirWindfield/cargo-create. The name is a little bit misleading, as the tool is more of a general purpose project template generator than one specific for cargo. It does, however, register two binaries to make it more ergonomic inside the cargo workflow. It doesn't contain much right now. It has some default template variables that get filled in (system, datetime, author and project name). But it does support the The crate isn't meant as competition or anything similar. It was more meant to be a workspace that I can use to try out some crates that I wanted to use in other projects ( However, it does work without major problems (that I found) and I use it for project generation. This issue can easily be expanded with stuff that builds on top of this. For example patches that could be run (template engines are quite powerful these days though, so this might not be worth it). Conditional features that are enabled as soon as another feature is enabled as well, kind of creating chains. If you want to give it a go:
I am not sure why |
This is also an interesting features for project templates in embedded Rust: A single project template might support a number of different microcontrollers or variants of MCUs which currently has to be configured by hand after generating the project (see The OP in this issue proposes a purely command-line flag based approach but as |
@Rahix I could see #17 expanded with a |
#17 (Custom Variables), fully supports several types, including a bool that can be used for this exact purpose. |
There is a way to make conditional decisions within one file, not to include files conditionally. {% if network_enabled %}
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("0.0.0.0:8080").unwrap();
loop {
let (conn, addr) = listener.accept().unwrap();
println!("Incoming Connection from {}", addr);
std::io::copy(&mut &conn, &mut &conn).unwrap();
}
}
{% else %}
fn main() {
println!("Hello Rusty Hermit 🦀");
}
{% endif %} the only missing thing is a way to supply the variable via a dedicated cli argument. |
Variables can be supplied via cli arguments now. But we still need a way to selectively include files don't we (or maybe as a filter upon |
I actually like the idea of doing this in the [template]
cargo_generate_version = ">=0.9.0"
# default include/exclude goes here
[placeholders]
# default placeholders go here
[feature.default]
# "default" feature include/exclude goes here, will be added to the default include/exclude above
[feature.default.placeholders]
# additional placeholders for "default" feature goes here, will be added to the default placeholders above
[feature.foo]
uses = [ "default" ]
# "foo" feature include/exclude goes here, will be added to those of feature "defaults"
[feature.foo.placeholders]
# additional placeholders for "foo" feature goes here, will be added to those of the "default" feature then we could do stuff like cargo generate favorite --feature foo and maybe even cargo generate favorite --list-features to list the possible features. |
I think adding features just to include files conditionally is overkill since we already have custom variables. Example use caseSay that we want to include conditionally
If we implement "conditional file includes" with features instead, in this case I would need to add an extra feature to include What syntax should we use in
|
I liked the above solution so much, I made PR #431 . it basically allows everything described by @MarcoIeni above, while also allowing us to conditionally ignore files and have conditional placeholders. In order to facilitate this, I made use of the liquid engine and a little toml. Example: [template]
cargo_generate_version = ">=0.9.0"
# include/exclude/ignore also allowed here
[placeholders]
# common placeholders goes here
[conditional.'{% if crate_type == "bin" %}true{% endif %}']
ignore = [ "lib.rs" ]
# include/exclude also allowed here
[conditional.'{{var|truthy}}'.placeholders]
# placeholders for when {{var}} is considered false goes here The inclusion of From what I can tell, this works really well, and allows for a lot of flexibility in regards to what files are ignored/included/excluded and what variables will be queried. |
Great, thanks! conditional filesSo files are not ignored if the file is empty, but if they are listed in the [conditional.'{% if crate_type == "bin" %}true{% endif %}']
ignore = [ "lib.rs" ] right? Second question: Can you list also directories in Third question: [[conditional_ignore]]
condition = { key = "crate_type", value = "bin" }
ignore = [ "lib.rs" ]
[[conditional_ignore]]
condition = { key = "crate_type", value = "lib" }
ignore = [ "main.rs" ]
[[conditional_ignore]]
condition = { key = "my_bool_var", value = true }
ignore = [ "subdir/foo.rs" ] conditional placeholders[conditional.'{{var|truthy}}.placeholders]
# placeholders for when {{var}} is considered false goes here As I commented in the PR, the problem I see is that this doesn't support string comparison in the condition. EDIT: probably |
EDIT: For the first question, you are correct; |
Thanks for the clarifications. |
What do you think of surrounding the parts of toml we want to add conditionally with the liquid condition? Something like this: {% if crate_type == "bin" %}
ignore = [ "lib.rs" ]
# include/exclude also allowed here
{% endif %}
{% if var == "true" %}
[placeholders.my_var]
# my_var data, which is included when {{var}} is considered true
{% endif %} |
That leaves a problem, as in order to render the toml file we would need to read and parse it first, which we can't as it's not legal toml until after the render! The file still contains settings that are, or may become, needed before we can resolve the conditions - an example is placeholders for the variables that the conditions use. So, we would need to read the toml file in order to resolve it. TBH, I think the only way to do that would require us to write our own toml parser/alternate syntax. Doing so would also make sure we can not use any of the nice toml syntax highligters etc. that exist for all larger editors - so no, I do not recommend this route atm. |
@MarcoIeni [conditional.'{{crate_type|is_bin}}']
# section used when expanding with option --bin
[conditional.'{{crate_type|is_lib}}']
# section used when expanding with option --lib
[conditional.'{{crate_type|is_macro|falsy}}']
# filters can be mixed... this section is when crate_type != "proc-macro" at least to me, when having these filters, this is just as readable as the stuff we have to write in |
Nice! What about |
Yeah, I might have forgotten that one - there's really no way of setting |
It would be really cool if
cargo-generate
would have something like cargo's features, where a user could select to include certain configuration/setup files of a template if he wants to.An example: My template contains GitHub Actions workflow files for creating releases on tag pushes on master as well as creating documentation on the
gh-pages
branch when a push to master happens. The template would, by default, include everything. But if a user doesn't want the docs workflow, he could run something similar to this:The first is a somewhat nicer syntax for this use-case I think. The second one would be the same as the one from cargo.
I imagine something like this inside the configuration file:
This comes really handy in situations where templates offer a lot of pre-defined configuration files with sane and good default values for various services (GitHub Actions, bors etc). That way, a user can just pick what he wants and be done.
Feedback is appreciated!
Edit: Additionally, this could be expanded upon composing features into "super-features". For example:
This would introduce more code to maintain, as something like cyclic dependency checks have to be performed on the features before running them.
The text was updated successfully, but these errors were encountered: