Skip to content

Commit

Permalink
Allow git subfolder as a template.
Browse files Browse the repository at this point in the history
Using cargo generate --git <repo>#<sub-folder, this one
1) Allows to use a subfolder as the actual template (see #47)
2) Allows to e.g. generate a project from an example in a repo (see #78)
3) Avoids the conflicting requirements for cargo-generate#295 and cargo-generate#291, allowing for easier implementation of `--pick` for cargo-generate#291.
  • Loading branch information
taurr committed Jul 27, 2021
1 parent 4b42337 commit 05a8a83
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 130 deletions.
116 changes: 71 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ See the [`openssl-sys` crate readme] on how to obtain the OpenSSL library for yo
### Using `cargo` with vendored OpenSSL

> NOTE: `vendored-openssl` requires the following packages to be installed:
> - libssl-dev
> - gcc
> - m4
> - ca-certificates
> - make
> - perl
>
> - libssl-dev
> - gcc
> - m4
> - ca-certificates
> - make
> - perl
```sh
cargo install cargo-generate --features vendored-openssl
Expand All @@ -50,7 +51,7 @@ cargo install cargo-generate --features vendored-openssl

Standard usage is to pass a `--git` flag to `cargo generate` or short `cargo gen`. This will prompt you to enter the name of your project.

> NOTE: `cargo gen` requires an [cargo alias configuration](#cargo-gen---alias)
> NOTE: `cargo gen` requires an [cargo alias configuration](#cargo-gen---alias)
```sh
cargo generate --git https://github.com/githubusername/mytemplate.git
Expand All @@ -62,18 +63,33 @@ You can also pass the name of your project to the tool using the `--name` or `-n
cargo generate --git https://github.com/githubusername/mytemplate.git --name myproject
```

If the git repository contains multiple templates, the specific subfolder in the git repository may be specified like this:

```sh
cargo generate --git "https://github.com/githubusername/mytemplate.git <relative-template-path>"
```

> NOTE: The specified `relative-template-path` will be used as the actual template root, whether or not this is actually true!
> NOTE: When using the `subfolder` feature, `cargo-generate` will search for
## git over ssh

New in version [0.7.0] is the support for both public and private and ssh git remote urls.
For example:

```sh
cargo generate --git [email protected]:rustwasm/wasm-pack-template.git --name mywasm
```

leads to the same result as:

```sh
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name mywasm
```

as well as:

```sh
cargo generate --git rustwasm/wasm-pack-template --name mywasm
```
Expand All @@ -82,24 +98,25 @@ cargo generate --git rustwasm/wasm-pack-template --name mywasm
## http(s) proxy

New in version [0.7.0] is automatic proxy usage. So, if http(s)_PROXY env variables are provided, they
will be used for cloning a http(s) template repository.
New in version [0.7.0] is automatic proxy usage. So, if http(s)\_PROXY env variables are provided, they
will be used for cloning a http(s) template repository.

## Favorites

Favorite templates can be defined in a config file, that by default is placed at `$CARGO_HOME/cargo-generate`.
Favorite templates can be defined in a config file, that by default is placed at `$CARGO_HOME/cargo-generate`.
To specify an alternative configuration file, use the `--config <config-file>` option.

Each favorite template is specified in its own section, e.g.:

```toml
[favorites.demo]
description = "Demo template for cargo-generate"
description = "<optional description, visible with --list-favorites>"
git = "https://github.com/ashleygwilliams/wasm-pack-template"
branch = "master"
branch = "<optional-branch>"
subfolder = "<optional-subfolder>"
```

Both `branch` and `description` are optional, and the branch may be overridden by specifying `--branch <branch>` on the command line.
Values may be overridden using the CLI arguments of the same names (e.g. `--subfolder` for the `subfolder` value).

When favorites are available, they can be generated simply by invoking:

Expand All @@ -110,7 +127,7 @@ cargo gen <favorite>
or slightly more involved:

```cli
cargo generate demo --branch master --name expanded_demo
cargo generate demo --branch mybranch --name expanded_demo --subfolder myfolder
```

> NOTE: when `<favorite>` is not defined in the config file, it is interpreted as a git repo like as if `--git <favorite>`
Expand All @@ -120,23 +137,27 @@ cargo generate demo --branch master --name expanded_demo
Templates are git repositories whose files contain placeholders. The current
supported placeholders are:

- `{{authors}}`

this will be filled in by a function borrowed from Cargo's source code, that determines your information from Cargo's configuration.
- `{{project-name}}`

this is supplied by either passing the `--name` flag to the command or working with the interactive CLI to supply a name.
- `{{crate_name}}`

the snake_case_version of `project-name`
- `{{crate_type}}`

this is supplied by either passing the `--bin` or `--lib` flag to the command line, contains either `bin` or `lib`, `--bin` is the default
- `{{os-arch}}`

contains the current operating system and architecture ex: `linux-x86_64`

Additionally, **all filters and tags** of the liquid template language are supported.
- `{{authors}}`

this will be filled in by a function borrowed from Cargo's source code, that determines your information from Cargo's configuration.

- `{{project-name}}`

this is supplied by either passing the `--name` flag to the command or working with the interactive CLI to supply a name.

- `{{crate_name}}`

the snake_case_version of `project-name`

- `{{crate_type}}`

this is supplied by either passing the `--bin` or `--lib` flag to the command line, contains either `bin` or `lib`, `--bin` is the default

- `{{os-arch}}`

contains the current operating system and architecture ex: `linux-x86_64`

Additionally, **all filters and tags** of the liquid template language are supported.
For more information, check out the [Liquid Documentation on `Tags` and `Filters`][liquid].

[liquid]: https://shopify.github.io/liquid
Expand All @@ -148,9 +169,9 @@ The `.genignore` file is always ignored, so there is no need to list it in the `
Here's a list of [currently available templates](TEMPLATES.md).
If you have a great template that you'd like to feature here, please [file an issue or a PR]!

[file an issue or a PR]: https://github.com/cargo-generate/cargo-generate/issues
[file an issue or a pr]: https://github.com/cargo-generate/cargo-generate/issues

### Example for `--bin` and `--lib`
### Example for `--bin` and `--lib`

A template could be prepared in a way to act as a binary or a library. For example the `Cargo.toml` might look like:

Expand All @@ -171,16 +192,16 @@ name = "{{crate_name}}-cli"
{% endif %}
```

Now a user of this template could decide weather they want the binary version by passing `--bin`
Now a user of this template could decide weather they want the binary version by passing `--bin`
or use only the library version by passing `--lib` as a command line argument.

## Template defined placeholders

Sometimes templates need to make decisions. For example one might want to conditionally include some code or not.
Sometimes templates need to make decisions. For example one might want to conditionally include some code or not.
Another use case might be that the user of a template should be able to choose out of provided options in an interactive way.
Also, it might be helpful to offer a reasonable default value that the user just simply can use.

Since version [0.6.0](https://github.com/cargo-generate/cargo-generate/releases/tag/v0.6.0) it is possible to use placeholders in a `cargo-generate.toml` that is in the root folder of a template.
Since version [0.6.0](https://github.com/cargo-generate/cargo-generate/releases/tag/v0.6.0) it is possible to use placeholders in a `cargo-generate.toml` that is in the root folder of a template.
Here [an example](https://github.com/sassman/hermit-template-rs):

```toml
Expand Down Expand Up @@ -220,6 +241,7 @@ fn main() {
```

> Tip: similar to `dependencies` in the `Cargo.toml` file you can also list them as one liners:
```toml
[placeholders]
hypervisor = { type = "string", prompt = "What hypervisor to use?", choices = ["uhyve", "qemu"], default = "qemu" }
Expand All @@ -240,7 +262,7 @@ A placeholder can be of type `string` or `bool`. Boolean types are usually helpf

### `choices` property (optional)

A placeholder can come with a list of choices that the user can choose from.
A placeholder can come with a list of choices that the user can choose from.
It's further also validated at the time when a user generates a project from a template.

```toml
Expand All @@ -258,12 +280,13 @@ default = 'qemu'

### `regex` property (optional)

A `regex` property is a string, that can be used to enforce a certain validation rule. The input dialog will keep repeating
A `regex` property is a string, that can be used to enforce a certain validation rule. The input dialog will keep repeating
until the user entered something that is allowed by this regex.

### Placeholder Examples

An example with a regex that allows only numbers

```toml
[placeholders]
phone_number = { type = "string", prompt = "What's your phone number?", regex = "[0-9]+" }
Expand All @@ -284,8 +307,9 @@ network_enabled = true
## Include / Exclude

Templates support a `cargo-generate.toml`, with a "template" section that allows you to configure the files that will be processed by `cargo-generate`.
The behavior mirrors Cargo's Include / Exclude functionality, which is [documented here](https://doc.rust-lang.org/cargo/reference/manifest.html#the-exclude-and-include-fields-optional).
If you are using placeholders in a file name, and also wish to use placeholders in the contents of that file,

The behavior mirrors Cargo's Include / Exclude functionality, which is [documented here](https://doc.rust-lang.org/cargo/reference/manifest.html#the-exclude-and-include-fields-optional).
If you are using placeholders in a file name, and also wish to use placeholders in the contents of that file,
you should setup your globs to match on the pre-rename filename.

```toml
Expand All @@ -295,9 +319,11 @@ include = ["Cargo.toml"]
exclude = ["*.c"]
```

The `cargo-generate.toml` file should be placed in the root of the template. If using the `subfolder` feature, the root is the `subfolder` inside the repository, though `cargo-generate` will look for the file in all parent folders until it reaches the repository root.

## Cargo gen - alias

`cargo gen` requires an [cargo alias](https://doc.rust-lang.org/cargo/reference/config.html)
`cargo gen` requires an [cargo alias](https://doc.rust-lang.org/cargo/reference/config.html)
to be configured in your `$HOME/.cargo/config` like this:

```toml
Expand All @@ -309,8 +335,8 @@ gen = "generate"

Licensed under either of

* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

Expand All @@ -322,5 +348,5 @@ license, shall be dual licensed as above, without any additional terms or
conditions.
If you want to contribute to `cargo-generate`, please read our [CONTRIBUTING notes].

[CONTRIBUTING notes]: CONTRIBUTING.md
[0.7.0]: https://github.com/cargo-generate/cargo-generate/releases/tag/v0.7.0
[contributing notes]: CONTRIBUTING.md
[0.7.0]: https://github.com/cargo-generate/cargo-generate/releases/tag/v0.7.0
1 change: 1 addition & 0 deletions src/app_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) struct FavoriteConfig {
pub description: Option<String>,
pub git: Option<String>,
pub branch: Option<String>,
pub subfolder: Option<String>,
}

impl Default for AppConfig {
Expand Down
24 changes: 15 additions & 9 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,21 @@ impl TryFrom<String> for Config {
}

impl Config {
pub(crate) fn new<P: AsRef<Path>>(path: P) -> Result<Option<Self>> {
match fs::read_to_string(path) {
Ok(contents) => Config::try_from(contents)
.map(Option::from)
.map_err(|e| e.into()),
Err(e) => match e.kind() {
ErrorKind::NotFound => Ok(None),
_ => anyhow::bail!(e),
pub(crate) fn from_path<P>(path: &Option<P>) -> Result<Option<Self>>
where
P: AsRef<Path>,
{
match path {
Some(path) => match fs::read_to_string(path) {
Ok(contents) => Config::try_from(contents)
.map(Option::from)
.map_err(|e| e.into()),
Err(e) => match e.kind() {
ErrorKind::NotFound => Ok(None),
_ => anyhow::bail!(e),
},
},
None => Ok(None),
}
}
}
Expand Down Expand Up @@ -89,7 +95,7 @@ mod tests {
)
.unwrap();

let config = Config::new(&config_path).unwrap().unwrap();
let config = Config::from_path(&Some(config_path)).unwrap().unwrap();

assert_eq!(
config.template,
Expand Down
15 changes: 13 additions & 2 deletions src/favorites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub(crate) fn list_favorites(args: &Args) -> Result<()> {

pub(crate) fn resolve_favorite_args(args: &mut Args) -> Result<()> {
if args.git.is_some() {
args.subfolder = args.favorite.take();
return Ok(());
}

Expand All @@ -48,7 +49,7 @@ pub(crate) fn resolve_favorite_args(args: &mut Args) -> Result<()> {
let app_config_path = app_config_path(&args.config)?;
let app_config = AppConfig::from_path(app_config_path.as_path())?;

let (git, branch) = app_config
let (git, branch, subfolder) = app_config
.favorites
.get(favorite_name.as_str())
.map_or_else(
Expand All @@ -57,17 +58,27 @@ pub(crate) fn resolve_favorite_args(args: &mut Args) -> Result<()> {
"Favorite {} not found in config, using it as a git repo url",
style(&favorite_name).bold()
);
(Some(favorite_name.clone()), args.branch.as_ref().cloned())
(
Some(favorite_name.clone()),
args.branch.as_ref().cloned(),
args.subfolder.clone(),
)
},
|f| {
(
f.git.clone(),
args.branch.as_ref().or_else(|| f.branch.as_ref()).cloned(),
args.subfolder
.as_ref()
.or_else(|| f.subfolder.as_ref())
.cloned(),
)
},
);

args.git = git;
args.branch = branch;
args.subfolder = subfolder;

Ok(())
}

0 comments on commit 05a8a83

Please sign in to comment.