Looking for thoughts on an alternative idea to what we normally think of as templates for cargo new and some brainstorming on the CLI.
From both the Cargo team and the community, there has long been an interest to having cargo generate-like templates in cargo new (Feature request: Cargo templates · Issue #5151 · rust-lang/cargo · GitHub). However, this is not trivial and has needed someone to work through problems like:
- Working out the template language to pull into our compatibility guarantees
- Deciding on the API surface between
cargo newand the template, either being exhaustive or having a plan for evolving it - Should we and how do we preserve the "smart" features like auto-inheriting
- How to handle updating from "living" templates. All of my repos have a common git merge base that is the template and this makes it easy to propagate changes out, like with lints. I couldn't live without it
- How to handle composition. There are project templates, and package templates and how to allow intermixing of them
On that last point, in other discussions I've realized that there is another layer of templates: API templates. These are not whole-package templates but build-target or even mod-specific templates. You might have a CLI template that gets you started with CLI parser, a logger, etc.You might then have a CLI test template that does end-to-end testing of your CLI, capturing SVG snapshots of the output to preserve the colors. You then might find you need to do file watching so you want to pull in a notify template as a mod.
My idea has two parts:
cargo newbecomes additive, allowingcargo new --libwhen there is already a package present, adding a lib- We allow specifying a package's examples as your
src/lib.rsorsrc/main.rs. Even better if it could also bebuild.rsandtests/foo.rs. Amazing if we could also dosrc/foo/mod.rs.
This way you can pull in examples as you need the behavior and then start editing from there to get what you want.
If we limit this to examples you already have as dependencies, then we could look at the examples required-features and automatically add the features and dependencies that that activates. An example can also rely on dev-dependencies but we don't have a good story for detecting which are relevant and likely would have to just let those example-templates be broken. Pulling in a frontmatter might also be a way to customize the dependencies. This can also help users reading the example have a more precise dependency list. The downside is that there wouldn't be an easy way to validate this
Sometimes there are other settings that are relevant, like harness = false. If the person has tests or benches enabled for their example, we could read that and copy it over to their package.
Limiting to existing dependencies makes this a bit easier to operate on. However, if we could find a way to make this also work with any package, then this would be amazing for Cargo Script. A big priority for Cargo Script is that it should be light weight enough not to need cargo new support. I've intentionally avoided it to help draw attention to the pain points involved in hand writing them so we can address them.
However, if cargo new could give you more than
#!/usr/bin/env cargo
---
package.edition = "2024"
---
fn main() {
}
and instead could give you
#!/usr/bin/env cargo
---
package.edition = "2024"
[dependencies]
clap = { version = "4.5.30", features = ["derive"] }
---
use clap::Parser;
#[derive(Parser)]
struct Cli {
}
fn main() {
let cli = Cli::parse();
}
That would be much more powerful.
However, where I'm stuck on this idea is how to design the CLI, keeping in mind
- build-targets are additive
- we'd support more kinds of build targets
- ideally, also support mods
- select a dependency
- select the example within the dependency, helping the user choose
- maybe even allow this for non-dependencies
- align with Cargo's CLI principles
- fit within
cargo new
Unresolved questions
- Could we somehow have descriptions for showing the user, e.g. in an interactive mode?
- Could maintainers specify a default example?
- How do we handle this on the CLI? If we use a variable
num_argsin clap, the positional argument can be mistaken for an example
- How do we handle this on the CLI? If we use a variable
- What level of built-in validation should we provide vs being done by third-party tools (kind of like the role
cargo hackplays)