Consilidate template libraries #186

Merged
lucas merged 2 commits from issue/124 into master 2024-08-21 11:49:19 +02:00
5 changed files with 59 additions and 36 deletions

11
Cargo.lock generated
View file

@ -937,6 +937,7 @@ dependencies = [
"futures-util", "futures-util",
"glob", "glob",
"luajit2-sys", "luajit2-sys",
"minijinja",
"nanorand", "nanorand",
"notify", "notify",
"oodle", "oodle",
@ -948,7 +949,6 @@ dependencies = [
"serde", "serde",
"serde_sjson", "serde_sjson",
"shlex", "shlex",
"string_template",
"tempfile", "tempfile",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
@ -3307,15 +3307,6 @@ dependencies = [
"float-cmp", "float-cmp",
] ]
[[package]]
name = "string_template"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f2c6b2c3fa950895c9aeb0c3cb9271d7eb580662af9af2b711b593f446320"
dependencies = [
"regex",
]
[[package]] [[package]]
name = "strip-ansi-escapes" name = "strip-ansi-escapes"
version = "0.2.0" version = "0.2.0"

View file

@ -14,6 +14,7 @@ exclude = ["lib/color-eyre"]
[workspace.dependencies] [workspace.dependencies]
zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] }
minijinja = { version = "2.0.1", default-features = false }
[patch.crates-io] [patch.crates-io]
color-eyre = { path = "lib/color-eyre" } color-eyre = { path = "lib/color-eyre" }

View file

@ -27,7 +27,7 @@ futures = "0.3.25"
interprocess = "2.1.0" interprocess = "2.1.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" }
minijinja = { version = "2.0.1", default-features = false } minijinja = { workspace = true }
nexusmods = { path = "../../lib/nexusmods", version = "*" } nexusmods = { path = "../../lib/nexusmods", version = "*" }
oodle = { path = "../../lib/oodle", version = "*" } oodle = { path = "../../lib/oodle", version = "*" }
open = "5.0.1" open = "5.0.1"

View file

@ -20,7 +20,7 @@ promptly = "0.3.1"
sdk = { path = "../../lib/sdk", version = "*" } sdk = { path = "../../lib/sdk", version = "*" }
serde_sjson = { path = "../../lib/serde_sjson", version = "*" } serde_sjson = { path = "../../lib/serde_sjson", version = "*" }
serde = { version = "1.0.147", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] }
string_template = "0.2.1" minijinja = { workspace = true }
tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] }
tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] }
tracing-error = "0.2.0" tracing-error = "0.2.0"

View file

@ -1,18 +1,30 @@
use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use color_eyre::eyre::{self, Context, Result}; use color_eyre::eyre::{self, Context, Result};
use color_eyre::Help; use color_eyre::Help;
use futures::{StreamExt, TryStreamExt}; use futures::{StreamExt, TryStreamExt};
use string_template::Template; use minijinja::Environment;
use tokio::fs::{self, DirBuilder}; use tokio::fs::{self, DirBuilder};
const TEMPLATES: [(&str, &str); 5] = [ const TEMPLATES: [(&str, &str); 5] = [
( (
"dtmt.cfg", "dtmt.cfg",
r#"id = "{{id}}" r#"//
// This is your mod's main configuration file. It tells DTMT how to build the mod,
// and DTMM what to display to your users.
// Certain files have been pre-filled by the template, the ones commented out (`//`)
// are optional.
//
// A unique identifier (preferably lower case, alphanumeric)
id = "{{id}}"
// The display name that your users will see.
// This doesn't have to be unique, but you still want to avoid being confused with other
// mods.
name = "{{name}}" name = "{{name}}"
// It's good practice to increase this number whenever you publish changes.
// It's up to you if you use SemVer or something simpler like `1970-12-24`. It should sort and
// compare well, though.
version = "0.1.0" version = "0.1.0"
// author = "" // author = ""
@ -32,16 +44,25 @@ categories = [
// A list of mod IDs that this mod depends on. You can find // A list of mod IDs that this mod depends on. You can find
// those IDs by downloading the mod and extracting their `dtmt.cfg`. // those IDs by downloading the mod and extracting their `dtmt.cfg`.
// To make your fellow modders' lives easier, publish your own mods' IDs
// somewhere visible, such as the Nexusmods page.
depends = [ depends = [
DMF DMF
] ]
// The primary resources that serve as the entry point to your
// mod's code. Unless for very specific use cases, the generated
// values shouldn't be changed.
resources = { resources = {
init = "scripts/mods/{{id}}/init" init = "scripts/mods/{{id}}/init"
data = "scripts/mods/{{id}}/data" data = "scripts/mods/{{id}}/data"
localization = "scripts/mods/{{id}}/localization" localization = "scripts/mods/{{id}}/localization"
} }
// The list of packages, or bundles, to build.
// Each one corresponds to a package definition in the named folder.
// For mods that contain only code and/or a few small assets, a single
// package will suffice.
packages = [ packages = [
"packages/mods/{{id}}" "packages/mods/{{id}}"
] ]
@ -59,7 +80,6 @@ packages = [
r#"local mod = get_mod("{{id}}") r#"local mod = get_mod("{{id}}")
-- Your mod code goes here. -- Your mod code goes here.
-- https://vmf-docs.verminti.de
"#, "#,
), ),
( (
@ -137,34 +157,45 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()>
tracing::debug!(root = %root.display(), name, id); tracing::debug!(root = %root.display(), name, id);
let mut data = HashMap::new(); let render_ctx = minijinja::context!(name => name.as_str(), id => id.as_str());
data.insert("name", name.as_str()); let env = Environment::new();
data.insert("id", id.as_str());
let templates = TEMPLATES let templates = TEMPLATES
.iter() .iter()
.map(|(path_tmpl, content_tmpl)| { .map(|(path_tmpl, content_tmpl)| {
let path = Template::new(path_tmpl).render(&data); env.render_str(path_tmpl, &render_ctx)
let content = Template::new(content_tmpl).render(&data); .wrap_err_with(|| format!("Failed to render template: {}", path_tmpl))
.and_then(|path| {
(root.join(path), content) env.render_named_str(&path, content_tmpl, &render_ctx)
.wrap_err_with(|| format!("Failed to render template '{}'", &path))
.map(|content| (root.join(path), content))
})
}) })
.map(|(path, content)| async move { .map(|res| async move {
let dir = path match res {
.parent() Ok((path, content)) => {
.ok_or_else(|| eyre::eyre!("invalid root path"))?; let dir = path
.parent()
.ok_or_else(|| eyre::eyre!("invalid root path"))?;
DirBuilder::new() DirBuilder::new()
.recursive(true) .recursive(true)
.create(&dir) .create(&dir)
.await .await
.wrap_err_with(|| format!("Failed to create directory {}", dir.display()))?; .wrap_err_with(|| {
format!("Failed to create directory {}", dir.display())
})?;
tracing::trace!("Writing file {}", path.display()); tracing::trace!("Writing file {}", path.display());
fs::write(&path, content.as_bytes()) fs::write(&path, content.as_bytes())
.await .await
.wrap_err_with(|| format!("Failed to write content to path {}", path.display())) .wrap_err_with(|| {
format!("Failed to write content to path {}", path.display())
})
}
Err(e) => Err(e),
}
}); });
futures::stream::iter(templates) futures::stream::iter(templates)