Skip to content

Commit bec1aca

Browse files
authored
Update (#62)
* Update clap and edition * clippy * update readme * Update images for CI * Update images for build as well
1 parent 8c6aec3 commit bec1aca

File tree

10 files changed

+731
-329
lines changed

10 files changed

+731
-329
lines changed

Cargo.lock

Lines changed: 634 additions & 221 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
[package]
22
authors = ["Vincent Prouillet <hello@vincentprouillet.com>"]
33
description = "A simple way to get started with a project by scaffolding from a template powered by the Tera engine"
4-
edition = "2018"
4+
edition = "2021"
55
keywords = ["tera", "scaffolding", "templating", "generator", "boilerplate"]
66
license = "MIT"
77
name = "kickstart"
8-
version = "0.3.0"
8+
version = "0.4.0"
99

1010
[dependencies]
11-
clap = "2"
11+
clap = { version = "4", features = ["derive"] }
1212
glob = "0.3"
1313
memchr = "2"
1414
regex = "1"
1515
serde = {version = "1", features = ["derive"]}
1616
tera = "1"
1717
heck = "0.4.0"
1818
term = "0.7"
19-
toml = "0.5"
19+
toml = "0.7"
2020
walkdir = "2"
2121

2222
[dev-dependencies]

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,12 @@ You can use these like any other filter, e.g. `{{variable_name | camel_case}}`.
194194

195195
## Changelog
196196

197-
### 0.3.0 (unreleased)
197+
### 0.4.0 (unrelased)
198+
199+
- Add case conversion filter
200+
- Update dependencies
201+
202+
### 0.3.0 (2021-07-10)
198203

199204
- Update dependencies
200205

azure-pipelines.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ stages:
1111
strategy:
1212
matrix:
1313
windows-stable:
14-
imageName: 'vs2017-win2016'
14+
imageName: 'windows-2022'
1515
rustup_toolchain: stable
1616
mac-stable:
17-
imageName: 'macos-10.14'
17+
imageName: 'macos-13'
1818
rustup_toolchain: stable
1919
linux-stable:
2020
imageName: 'ubuntu-latest'
2121
rustup_toolchain: stable
2222
linux-msrv:
2323
imageName: 'ubuntu-latest'
24-
rustup_toolchain: 1.40.0
24+
rustup_toolchain: 1.65.0
2525
pool:
2626
vmImage: $(imageName)
2727
steps:
@@ -52,11 +52,11 @@ stages:
5252
strategy:
5353
matrix:
5454
windows-stable:
55-
imageName: 'vs2017-win2016'
55+
imageName: 'windows-2022'
5656
rustup_toolchain: stable
5757
target: 'x86_64-pc-windows-msvc'
5858
mac-stable:
59-
imageName: 'macos-10.14'
59+
imageName: 'macos-13'
6060
rustup_toolchain: stable
6161
target: 'x86_64-apple-darwin'
6262
linux-stable:

src/bin/kickstart.rs

Lines changed: 54 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,42 @@
1-
use std::env;
21
use std::error::Error;
3-
use std::path::Path;
2+
use std::path::PathBuf;
43

5-
use clap::{crate_authors, crate_description, crate_version, App, AppSettings, Arg, SubCommand};
4+
use clap::{Parser, Subcommand};
65

76
use kickstart::generation::Template;
87
use kickstart::terminal;
98
use kickstart::validate::validate_file;
109

11-
pub fn build_cli() -> App<'static, 'static> {
12-
App::new("kickstart")
13-
.version(crate_version!())
14-
.author(crate_authors!())
15-
.about(crate_description!())
16-
.setting(AppSettings::SubcommandsNegateReqs)
17-
.arg(
18-
Arg::with_name("template")
19-
.required(true)
20-
.help("Template to use: a local path or a HTTP url pointing to a Git repository"),
21-
)
22-
.arg(
23-
Arg::with_name("output-dir")
24-
.short("o")
25-
.long("output-dir")
26-
.takes_value(true)
27-
.help("Where to output the project: defaults to the current directory"),
28-
)
29-
.arg(
30-
Arg::with_name("sub-dir")
31-
.short("s")
32-
.long("sub-dir")
33-
.takes_value(true)
34-
.help("A subdirectory of the chosen template to use, to allow nested templates."),
35-
)
36-
.arg(
37-
Arg::with_name("no-input")
38-
.long("no-input")
39-
.help("Do not prompt for parameters and only use the defaults from template.toml"),
40-
)
41-
.subcommands(vec![SubCommand::with_name("validate")
42-
.about("Validates that a template.toml is valid")
43-
.arg(Arg::with_name("path").required(true).help("The path to the template.toml"))])
10+
#[derive(Parser)]
11+
#[clap(version, author, about, subcommand_negates_reqs = true)]
12+
pub struct Cli {
13+
/// Template to use: a local path or a HTTP url pointing to a Git repository
14+
#[clap(required = true)]
15+
pub template: Option<String>,
16+
17+
/// Where to output the project: defaults to the current directory
18+
#[clap(short = 'o', long, default_value = ".")]
19+
pub output_dir: PathBuf,
20+
21+
/// A subdirectory of the chosen template to use, to allow nested templates.
22+
#[clap(short = 's', long)]
23+
pub sub_dir: Option<String>,
24+
25+
/// Do not prompt for parameters and only use the defaults from template.toml
26+
#[clap(long, default_value_t = false)]
27+
pub no_input: bool,
28+
29+
#[clap(subcommand)]
30+
pub command: Option<Command>,
31+
}
32+
33+
#[derive(Debug, Subcommand)]
34+
pub enum Command {
35+
/// Validates that a template.toml is valid
36+
Validate {
37+
/// The path to the template.toml
38+
path: PathBuf,
39+
},
4440
}
4541

4642
fn bail(e: &dyn Error) -> ! {
@@ -54,44 +50,32 @@ fn bail(e: &dyn Error) -> ! {
5450
}
5551

5652
fn main() {
57-
let matches = build_cli().get_matches();
53+
let cli = Cli::parse();
5854

59-
match matches.subcommand() {
60-
("validate", Some(matches)) => {
61-
let errs = match validate_file(matches.value_of("path").unwrap()) {
62-
Ok(e) => e,
63-
Err(e) => bail(&e),
64-
};
55+
if let Some(Command::Validate { path }) = cli.command {
56+
let errs = match validate_file(path) {
57+
Ok(e) => e,
58+
Err(e) => bail(&e),
59+
};
6560

66-
if !errs.is_empty() {
67-
terminal::error("The template.toml is invalid:\n");
68-
for err in errs {
69-
terminal::error(&format!("- {}\n", err));
70-
}
71-
::std::process::exit(1);
72-
} else {
73-
terminal::success("The template.toml file is valid!\n");
61+
if !errs.is_empty() {
62+
terminal::error("The template.toml is invalid:\n");
63+
for err in errs {
64+
terminal::error(&format!("- {}\n", err));
7465
}
66+
::std::process::exit(1);
67+
} else {
68+
terminal::success("The template.toml file is valid!\n");
7569
}
76-
_ => {
77-
// The actual generation call
78-
let template_path = matches.value_of("template").unwrap();
79-
let output_dir = matches
80-
.value_of("output-dir")
81-
.map(|p| Path::new(p).to_path_buf())
82-
.unwrap_or_else(|| env::current_dir().unwrap());
83-
let no_input = matches.is_present("no-input");
84-
let sub_dir = matches.value_of("sub-dir");
70+
} else {
71+
let template = match Template::from_input(&cli.template.unwrap(), cli.sub_dir.as_deref()) {
72+
Ok(t) => t,
73+
Err(e) => bail(&e),
74+
};
8575

86-
let template = match Template::from_input(template_path, sub_dir) {
87-
Ok(t) => t,
88-
Err(e) => bail(&e),
89-
};
90-
91-
match template.generate(&output_dir, no_input) {
92-
Ok(_) => terminal::success("\nEverything done, ready to go!\n"),
93-
Err(e) => bail(&e),
94-
};
95-
}
76+
match template.generate(&cli.output_dir, cli.no_input) {
77+
Ok(_) => terminal::success("\nEverything done, ready to go!\n"),
78+
Err(e) => bail(&e),
79+
};
9680
}
9781
}

src/definition.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
use std::collections::{HashMap};
1+
use std::collections::HashMap;
22

3-
use regex::{Regex, Match};
43
use serde::Deserialize;
5-
use tera::{Context};
4+
use tera::Context;
65
use toml::Value;
76

87
use crate::errors::{new_error, ErrorKind, Result};
98
use crate::prompt::{ask_bool, ask_choices, ask_integer, ask_string};
10-
use crate::utils::{render_one_off_template};
9+
use crate::utils::render_one_off_template;
1110

1211
/// A condition for a question to be asked
1312
#[derive(Debug, Clone, PartialEq, Deserialize)]
@@ -122,10 +121,10 @@ impl TemplateDefinition {
122121
context.insert(key, val);
123122
}
124123

125-
let rendered_default = render_one_off_template(&s, &context, None);
124+
let rendered_default = render_one_off_template(s, &context, None);
126125
match rendered_default {
127126
Err(e) => return Err(e),
128-
Ok(v ) => v,
127+
Ok(v) => v,
129128
}
130129
} else {
131130
s.clone()
@@ -303,7 +302,7 @@ mod tests {
303302
assert_eq!(tpl.variables.len(), 3);
304303

305304
let res = tpl.ask_questions(true);
306-
305+
307306
assert!(res.is_ok());
308307
let res = res.unwrap();
309308

@@ -314,6 +313,6 @@ mod tests {
314313
let got_value = res.get("manifest").unwrap();
315314
let expected_value: String = String::from("my_project-other_project-manifest.md");
316315

317-
assert_eq!(got_value, &Value::String(expected_value))
316+
assert_eq!(got_value, &Value::String(expected_value))
318317
}
319318
}

src/filters.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,35 @@ pub fn register_all_filters(tera: &mut Tera) {
1515

1616
pub fn upper_camel_case(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
1717
let s = try_get_value!("upper_camel_case", "value", String, value);
18-
Ok(to_value(&s.to_upper_camel_case()).unwrap())
18+
Ok(to_value(s.to_upper_camel_case()).unwrap())
1919
}
2020

2121
pub fn camel_case(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
2222
let s = try_get_value!("camel_case", "value", String, value);
23-
Ok(to_value(&s.to_lower_camel_case()).unwrap())
23+
Ok(to_value(s.to_lower_camel_case()).unwrap())
2424
}
2525

2626
pub fn snake_case(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
2727
let s = try_get_value!("snake_case", "value", String, value);
28-
Ok(to_value(&s.to_snake_case()).unwrap())
28+
Ok(to_value(s.to_snake_case()).unwrap())
2929
}
3030

3131
pub fn kebab_case(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
3232
let s = try_get_value!("kebab_case", "value", String, value);
33-
Ok(to_value(&s.to_kebab_case()).unwrap())
33+
Ok(to_value(s.to_kebab_case()).unwrap())
3434
}
3535

3636
pub fn shouty_snake_case(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
3737
let s = try_get_value!("shouty_snake_case", "value", String, value);
38-
Ok(to_value(&s.to_shouty_snake_case()).unwrap())
38+
Ok(to_value(s.to_shouty_snake_case()).unwrap())
3939
}
4040

4141
pub fn title_case(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
4242
let s = try_get_value!("title_case", "value", String, value);
43-
Ok(to_value(&s.to_title_case()).unwrap())
43+
Ok(to_value(s.to_title_case()).unwrap())
4444
}
4545

4646
pub fn shouty_kebab_case(value: &Value, _: &HashMap<String, Value>) -> Result<Value> {
4747
let s = try_get_value!("shouty_kebab_case", "value", String, value);
48-
Ok(to_value(&s.to_shouty_kebab_case()).unwrap())
48+
Ok(to_value(s.to_shouty_kebab_case()).unwrap())
4949
}

src/generation.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ use std::process::Command;
66
use std::str;
77

88
use glob::Pattern;
9-
use tera::{Context};
9+
use tera::Context;
1010
use walkdir::WalkDir;
1111

1212
use crate::definition::TemplateDefinition;
1313
use crate::errors::{map_io_err, new_error, ErrorKind, Result};
14-
use crate::utils::{render_one_off_template, create_directory, get_source, is_binary, read_file, write_file, Source};
14+
use crate::utils::{
15+
create_directory, get_source, is_binary, read_file, render_one_off_template, write_file, Source,
16+
};
1517

1618
/// The current template being generated
1719
#[derive(Debug, PartialEq)]
@@ -46,7 +48,7 @@ impl Template {
4648
// on some platforms:
4749
// https://www.reddit.com/r/rust/comments/92mbk5/kickstart_a_scaffolding_tool_to_get_new_projects/e3ahegw
4850
Command::new("git")
49-
.args(&["clone", "--recurse-submodules", remote, &format!("{}", tmp.display())])
51+
.args(["clone", "--recurse-submodules", remote, &format!("{}", tmp.display())])
5052
.output()
5153
.map_err(|err| new_error(ErrorKind::Git { err }))?;
5254
Ok(Template::from_local(&tmp, sub_dir))
@@ -79,15 +81,15 @@ impl Template {
7981

8082
if !output_dir.exists() {
8183
println!("Creating {:?}", output_dir);
82-
create_directory(&output_dir)?;
84+
create_directory(output_dir)?;
8385
}
8486

8587
// Create the glob patterns of files to copy without rendering first, only once
8688
let patterns: Vec<Pattern> =
8789
definition.copy_without_render.iter().map(|s| Pattern::new(s).unwrap()).collect();
8890

8991
let start_path = if let Some(ref directory) = definition.directory {
90-
self.path.join(&directory)
92+
self.path.join(directory)
9193
} else {
9294
self.path.clone()
9395
};
@@ -134,19 +136,19 @@ impl Template {
134136
}
135137

136138
// Only pass non-binary files or the files not matching the copy_without_render patterns through Tera
137-
let mut f = File::open(&entry.path())?;
139+
let mut f = File::open(entry.path())?;
138140
let mut buffer = Vec::new();
139141
f.read_to_end(&mut buffer)?;
140142

141143
let no_render = patterns.iter().map(|p| p.matches_path(&real_path)).any(|x| x);
142144

143145
if no_render || is_binary(&buffer) {
144-
map_io_err(fs::copy(&entry.path(), &real_path), entry.path())?;
146+
map_io_err(fs::copy(entry.path(), &real_path), entry.path())?;
145147
continue;
146148
}
147149

148150
let contents = render_one_off_template(
149-
&str::from_utf8(&buffer).unwrap(),
151+
str::from_utf8(&buffer).unwrap(),
150152
&context,
151153
Some(entry.path().to_path_buf()),
152154
)?;
@@ -158,7 +160,7 @@ impl Template {
158160
if let Some(val) = variables.get(&cleanup.name) {
159161
if *val == cleanup.value {
160162
for p in &cleanup.paths {
161-
let actual_path = render_one_off_template(&p, &context, None)?;
163+
let actual_path = render_one_off_template(p, &context, None)?;
162164
let path_to_delete = output_dir.join(actual_path);
163165
if !path_to_delete.exists() {
164166
continue;

0 commit comments

Comments
 (0)