@ssokolow Thanks for your detailed response! I think that we basically agree on everything except the details and the things that described too tersely to be understandable, which is nice. I especially agree with your points that the specific names that I used aren't self-descriptive (in particular I had a hard time even coming up with a name for target_root_dir
, so it makes sense that it'd be hard to understand what it means).
I'm going to go back and describe what I think are the N most common examples that template producers want to be able to express, which might help us come up with a better system than my original strawman proposal. Feel free to skip ahead to "Finishing up (open questions?)" where I think I list all the details that I'm not sure if we agree about. At this point I'm actually a little unclear what you think of my broader proposal
Quick definition: I'm going to use template repo
to refer to the root of a directory tree that has templates (so it's a template repository), it can be a plain local directory with no vcs or a remote version-controlled repository.
Example: Single template repo
In this case the ideal file system layout for the template producer probably looks something like:
./Cargo.toml
└─ src/some─files
And the Cargo.toml
probably includes nothing except for some {{ interpolation }}
keys.
I think that the features that would be required to make this work nicely would be to make cargo new --template
(or cargo from-template
, etc) capable of working with a directory that has no explicit template markers. That is, no Template.toml
or [template.*]
keys in Cargo.toml
.
AFAICT -- without significantly more advanced templating than has been proposed -- this has so little benefit over just a directory that you can directly clone that I'm not sure that it is worth supporting this case explicitly.
Example: Multiple templates in one repo
This is the example that motivates the target_root_dir
key, and also the one that introduces most of the conflicting pressures.
What I was imagining as being most desirable situation is a template author's directory tree that looks like:
$project
├── Cargo.toml
├── src/
│ └── lib.rs
└── templates/
├── one/
│ └── lib.rs
├── two/
│ └── (etc)
└── base/
└── Cargo.toml
Which, with a Cargo.toml
that includes:
[templates.default]
template_root_dir = "templates/one"
template_target_dir = "src"
overlay = ["base"]
[templates.two]
template_root_dir = "templates/two"
template_target_dir = "src"
overlay = ["base"]
[templates.base]
template_root_dir = "templates/base"
Would, given the (hypothetical) arguments cargo new --template $project
, produce:
.
├── Cargo.toml
└── src/
└── lib.rs
That is, the target_root_dir
is used to say "place the contents of this template directory relative to target_root_dir
instead of relative to the actual template root."
That is... hard to explain. Now that I've tried to explain it I think that it's almost certainly better to just require the correct directory in the template_root_dir
. So the above example would look like:
├── Cargo.toml
├── src/
│ └── lib.rs
└── templates/
├── one/
│ └── src/
│ └── lib.rs
├── two/
│ └── (etc)
└── base/
└── Cargo.toml
With a Cargo.toml
that has deleted its target_root_dir
it would look like:
[templates.default]
template_root_dir = "templates/one"
overlay = ["base"]
[templates.two]
template_root_dir = "templates/two"
overlay = ["base"]
[templates.base]
template_root_dir = "templates/base"
Oh, re-reading this I realize I just convinced myself of your argument that:
how is it so useful to generate a repo which is empty outside of src that it would justify a whole new config key when you could just put things in a subdirectory inside template_root_dir?
I should have read your post better.
Example: Using examples
This is identical to the previous case, except that we substitute examples/
everywhere that templates/
.
Finishing up (open questions?)
Mandatory template_root_dir
I think that whether this should default to .
or be a required parameter depends on if you think that the single-template repo is the common case. I don't think that it will be the common case, so I think it should be mandatory.
It also brings in questions about whether we should automatically strip Template.toml
(or template
keys in Cargo.toml
) when doing cargo new --template
. I think it's easier to document that no stripping happens (makes it easy to create a meta-template template repo!) and require that all templates be in sub-directories if they want to avoid including the template-related items. It's easy enough to specify .
.
Interpolation whitelist/blacklist
Regarding the whitelist/blacklist for interpolation: I think that your experience with just
hit a kind of surprising edge-case: most template languages use mustaches ({{
) for their template characters specifically because they're uncommon in all other programming languages.
I think that your problem would have been avoided if cargo-template
had produced a hard error on unknown keys instead of silently replacing them with the empty string. So I agree that it should be whitelist-only, and I think that the interpolation strings should be a hard error if their missing. But I haven't been convinced that something like interpolate_patterns = ["*"]
as the default is wrong. Especially given that I would like this to eventually support something like cookiecutter's cookiecutter.json
. I'm imagining something like this, eventually, being desired:
[template.default]
template_root_dir = "template"
[template.default.substitutions]
project = "my default project name"
listen_on = "127.0.0.1:3000"
# etc
This is after we have significantly more experience with the system. I'm not certain that it will be desired, but the competing pressures are a more conservative default now and a more verbose system in the long term.
Testing
It would probably be nice long term to build some features into cargo test
that guarantees that, if there are templates
keys in the Cargo.toml
(or if a Template.toml
exists) that generated templates can be successfully built, but I don't feel like specifying that right now.
The "template-name" cli parameter
My opinion isn't very strong. I have nightmares from explaining #egg_name=blah
to coworkers, so I don't want it to be a named parameter. But repo#non-default-name
seems basically okay, if not particularly self-documenting. It's shorter but I'm still partial to --template-name
. I wouldn't fight whoever implemented it if they disagreed with me
Template.toml verse [template] in Cargo.toml
I don't think anyone else has actually brought this up, but: I like that all cargo config happens inside of Cargo.toml
, so I'd prefer if it was keys inside Cargo.toml
.