feat: Implement command to create mod project from template
This commit is contained in:
parent
f61fab4257
commit
3b7abe02bf
5 changed files with 351 additions and 12 deletions
|
@ -6,6 +6,7 @@
|
|||
|
||||
- show status after adding dictionary entries
|
||||
- implement building mod bundles
|
||||
- implement command to create mod project from template
|
||||
|
||||
== [v0.2.0] - 2022-12-28
|
||||
|
||||
|
|
179
Cargo.lock
generated
179
Cargo.lock
generated
|
@ -29,6 +29,15 @@ dependencies = [
|
|||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -185,6 +194,17 @@ dependencies = [
|
|||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
"str-buf",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.2"
|
||||
|
@ -313,6 +333,16 @@ dependencies = [
|
|||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
|
@ -324,6 +354,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtmt"
|
||||
version = "0.2.0"
|
||||
|
@ -338,9 +379,11 @@ dependencies = [
|
|||
"libloading",
|
||||
"nanorand",
|
||||
"pin-project-lite",
|
||||
"promptly",
|
||||
"sdk",
|
||||
"serde",
|
||||
"serde_sjson",
|
||||
"string_template",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
|
@ -350,6 +393,12 @@ dependencies = [
|
|||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
|
@ -371,6 +420,16 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"str-buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.8"
|
||||
|
@ -390,6 +449,17 @@ dependencies = [
|
|||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "3.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb21c69b9fea5e15dbc1049e4b77145dd0ba1c84019c488102de0dc4ea4b0a27"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
|
@ -650,6 +720,15 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -683,6 +762,28 @@ version = "0.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.2"
|
||||
|
@ -837,6 +938,15 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "promptly"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9acbc6c5a5b029fe58342f58445acb00ccfe24624e538894bc2f04ce112980ba"
|
||||
dependencies = [
|
||||
"rustyline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
|
@ -846,6 +956,16 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||
dependencies = [
|
||||
"endian-type",
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
|
@ -878,6 +998,8 @@ version = "1.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
|
@ -925,12 +1047,42 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "9.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"dirs-next",
|
||||
"fd-lock",
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"scopeguard",
|
||||
"smallvec",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"utf8parse",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sdk"
|
||||
version = "0.2.0"
|
||||
|
@ -1037,6 +1189,21 @@ version = "1.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "str-buf"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
|
||||
|
||||
[[package]]
|
||||
name = "string_template"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f2c6b2c3fa950895c9aeb0c3cb9271d7eb580662af9af2b711b593f446320"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -1282,12 +1449,24 @@ version = "1.0.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -23,6 +23,8 @@ tracing-error = "0.2.0"
|
|||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
confy = "0.5.1"
|
||||
zip = "0.6.3"
|
||||
string_template = "0.2.1"
|
||||
promptly = "0.3.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.3.0"
|
||||
|
|
|
@ -1,19 +1,176 @@
|
|||
use clap::{Arg, ArgMatches, Command};
|
||||
use color_eyre::eyre::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(crate) fn _command_definition() -> Command {
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use color_eyre::eyre::{self, Context, Result};
|
||||
use color_eyre::Help;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use string_template::Template;
|
||||
use tokio::fs::{self, DirBuilder};
|
||||
|
||||
const TEMPLATES: [(&str, &str); 6] = [
|
||||
(
|
||||
"dtmt.cfg",
|
||||
r#"name = "{{name}}"
|
||||
description = "An elaborate description of my cool game mod!"
|
||||
version = "0.1.0"
|
||||
|
||||
packages = [
|
||||
"packages/{{name}}"
|
||||
]
|
||||
|
||||
depends = [
|
||||
"dmf"
|
||||
]
|
||||
"#,
|
||||
),
|
||||
(
|
||||
"{{name}}.mod",
|
||||
r#"return {
|
||||
run = function()
|
||||
fassert(rawget(_G, "new_mod"), "`{{title}}` encountered an error loading the Darktide Mod Framework.")
|
||||
|
||||
new_mod("{{name}}", {
|
||||
mod_script = "scripts/mods/{{name}}/{{name}}",
|
||||
mod_data = "scripts/mods/{{name}}/{{name}}_data",
|
||||
mod_localization = "scripts/mods/{{name}}/{{name}}_localization",
|
||||
})
|
||||
end,
|
||||
packages = {},
|
||||
}"#,
|
||||
),
|
||||
(
|
||||
"packages/{{name}}.package",
|
||||
r#"lua = [
|
||||
"scripts/mods/{{name}}/*"
|
||||
]
|
||||
"#,
|
||||
),
|
||||
(
|
||||
"scripts/mods/{{name}}/{{name}}.lua",
|
||||
r#"local mod = get_mod("{{name}}")
|
||||
|
||||
-- Your mod code goes here.
|
||||
-- https://vmf-docs.verminti.de
|
||||
"#,
|
||||
),
|
||||
(
|
||||
"scripts/mods/{{name}}/{{name}}_data.lua",
|
||||
r#"local mod = get_mod("{{name}}")
|
||||
|
||||
return {
|
||||
name = "{{title}}",
|
||||
description = mod:localize("mod_description"),
|
||||
is_togglable = true,
|
||||
}"#,
|
||||
),
|
||||
(
|
||||
"scripts/mods/{{name}}/{{name}}_localization.lua",
|
||||
r#"return {
|
||||
mod_description = {
|
||||
en = "An elaborate description of my cool game mod!",
|
||||
},
|
||||
}"#,
|
||||
),
|
||||
];
|
||||
|
||||
pub(crate) fn command_definition() -> Command {
|
||||
Command::new("new")
|
||||
.about("Create a new project")
|
||||
.arg(Arg::new("name").help(
|
||||
"The name of the new project. Will default to the name of the project's directory",
|
||||
))
|
||||
.arg(Arg::new("directory").required(true).help(
|
||||
"The directory where to initialize the new project. This directory must be empty\
|
||||
or must not exist. If `.` is given, the current directory will be used.",
|
||||
.arg(
|
||||
Arg::new("title")
|
||||
.long("title")
|
||||
.help("The display name of the new mod."),
|
||||
)
|
||||
.arg(Arg::new("root").help(
|
||||
"The directory where to initialize the new project. This directory must be empty \
|
||||
or must not exist. If omitted or `.` is given, the current directory \
|
||||
will be used.",
|
||||
))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn run(_ctx: sdk::Context, _matches: &ArgMatches) -> Result<()> {
|
||||
unimplemented!()
|
||||
pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> {
|
||||
let root = if let Some(dir) = matches.get_one::<String>("root") {
|
||||
if dir == "." {
|
||||
std::env::current_dir()
|
||||
.wrap_err("the current working dir is invalid")
|
||||
.with_suggestion(|| "Change to a different directory.")?
|
||||
} else {
|
||||
PathBuf::from(dir)
|
||||
}
|
||||
} else {
|
||||
let prompt = "The mod directory";
|
||||
match std::env::current_dir() {
|
||||
Ok(default) => promptly::prompt_default(prompt, default)?,
|
||||
Err(_) => promptly::prompt(prompt)?,
|
||||
}
|
||||
};
|
||||
|
||||
let title = if let Some(title) = matches.get_one::<String>("title") {
|
||||
title.clone()
|
||||
} else {
|
||||
promptly::prompt("The mod display name")?
|
||||
};
|
||||
|
||||
let name = {
|
||||
let default = title
|
||||
.chars()
|
||||
.map(|c| {
|
||||
if c.is_ascii_alphanumeric() {
|
||||
c.to_ascii_lowercase()
|
||||
} else {
|
||||
'_'
|
||||
}
|
||||
})
|
||||
.collect::<String>();
|
||||
promptly::prompt_default("The mod identifier name", default)?
|
||||
};
|
||||
|
||||
tracing::debug!(root = %root.display());
|
||||
tracing::debug!(title, name);
|
||||
|
||||
let mut data = HashMap::new();
|
||||
data.insert("name", name.as_str());
|
||||
data.insert("title", title.as_str());
|
||||
|
||||
let templates = TEMPLATES
|
||||
.iter()
|
||||
.map(|(path_tmpl, content_tmpl)| {
|
||||
let path = Template::new(path_tmpl).render(&data);
|
||||
let content = Template::new(content_tmpl).render(&data);
|
||||
|
||||
(root.join(path), content)
|
||||
})
|
||||
.map(|(path, content)| async move {
|
||||
let dir = path
|
||||
.parent()
|
||||
.ok_or_else(|| eyre::eyre!("invalid root path"))?;
|
||||
|
||||
DirBuilder::new()
|
||||
.recursive(true)
|
||||
.create(&dir)
|
||||
.await
|
||||
.wrap_err_with(|| format!("failed to create directory {}", dir.display()))?;
|
||||
|
||||
tracing::trace!("Writing file {}", path.display());
|
||||
|
||||
fs::write(&path, content.as_bytes())
|
||||
.await
|
||||
.wrap_err_with(|| format!("failed to write content to path {}", path.display()))
|
||||
});
|
||||
|
||||
futures::stream::iter(templates)
|
||||
.buffer_unordered(10)
|
||||
.try_fold((), |_, _| async { Ok(()) })
|
||||
.await?;
|
||||
|
||||
tracing::info!(
|
||||
"Created {} files for mod '{}' in '{}'.",
|
||||
TEMPLATES.len(),
|
||||
title,
|
||||
root.display()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ async fn main() -> Result<()> {
|
|||
.subcommand(cmd::bundle::command_definition())
|
||||
.subcommand(cmd::dictionary::command_definition())
|
||||
.subcommand(cmd::murmur::command_definition())
|
||||
// .subcommand(cmd::new::command_definition())
|
||||
.subcommand(cmd::new::command_definition())
|
||||
// .subcommand(cmd::watch::command_definition())
|
||||
.get_matches();
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue