Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
835 changes: 377 additions & 458 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ edition = "2024"
log = "0.4"
thiserror = "2"
anyhow = "1"
hashbrown = "0.15"
itertools = "0.14"
strum = { version = "0.27", features = ["derive"] }
enum-as-inner = "0.6"
modular-bitfield = "0.12"
hashbrown = "0.17"
itertools = "0.15"
strum = { version = "0.28", features = ["derive"] }
enum-as-inner = "0.7"
modular-bitfield = "0.13"
byteorder = "1"
flexstr = "0.9"
crc32fast = "1"
Expand Down
4 changes: 2 additions & 2 deletions scc/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ edition.workspace = true
[dependencies]
scc-lib = { path = "../lib" }
anyhow.workspace = true
bpaf = "0.7"
bpaf = "0.9"
minidl = "0.1"

[dev-dependencies]
winsplit = "0.1"
rstest = "0.24"
rstest = "0.28"
rstest_reuse = "0.7"
pretty_assertions = "1"

Expand Down
15 changes: 7 additions & 8 deletions scc/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
use std::process::ExitCode;

use anyhow::Context;
use bpaf::ParseFailure;
use opts::{Opts, fix_args};
use scc_lib::api::{SccOutput, SccResult, SccSettings};

Expand All @@ -19,13 +18,13 @@ fn main() -> ExitCode {
.as_slice(),
) {
Ok(opts) => opts,
Err(ParseFailure::Stdout(out)) => {
println!("{out}");
return ExitCode::SUCCESS;
}
Err(ParseFailure::Stderr(out)) => {
eprintln!("{out}");
return ExitCode::FAILURE;
Err(err) => {
err.print_message(100);
return if err.exit_code() == 0 {
ExitCode::SUCCESS
} else {
ExitCode::FAILURE
};
}
};

Expand Down
116 changes: 41 additions & 75 deletions scc/cli/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,98 +82,65 @@ fn is_not_slong(p: &PathBuf) -> bool {
}

fn no_space(name: &'static str, help: &'static str) -> impl Parser<String> {
any::<String>(name)
.help(help)
.guard(move |s| s.starts_with(name), "no_space doesn't start with -<name>")
.parse(move |s| s.strip_prefix(name).map(str::to_owned).ok_or("could not extract name"))
any(name, move |s: String| s.strip_prefix(name).map(str::to_owned))
.anywhere()
.help(help)
}

fn toggle_options(name: &'static str, help: &'static str) -> impl Parser<Option<bool>> {
any::<String>(name)
.help(help)
.guard(move |s| s.as_str() == name, "toggle_option isn't -<name>")
.map(|_| true)
.adjacent()
any(name, move |s: String| (s == name).then_some(true))
.anywhere()
.help(help)
.optional()
.catch()
}

fn equals_sign(name: &'static str, help: &'static str) -> impl Parser<Option<String>> {
any::<String>(name)
.help(help)
.guard(
move |s| s.strip_prefix(name).and_then(|s| s.strip_prefix('=')).is_some(),
"equals_sign doesn't start with -<name>=",
)
.parse(move |s| {
let (_, val) = s.split_once('=').ok_or("equals_sign does not contain =")?;
Ok::<_, &str>(val.to_owned())
})
.anywhere()
.optional()
.catch()
any(name, move |s: String| {
s.strip_prefix(name)
.and_then(|s| s.strip_prefix('='))
.map(str::to_owned)
})
.anywhere()
.help(help)
.optional()
.catch()
}

fn slong(name: &'static str, arg_name: &'static str, help: &'static str) -> impl Parser<Option<String>> {
let tag = any::<String>(name)
.help(help)
.guard(move |s| s.as_str() == name, "slong isn't -<name>");
let tag = literal(name).anywhere().help(help);
let value = positional::<String>(arg_name).guard(|s| !s.starts_with('-'), "starts with -");
construct!(tag, value)
.adjacent()
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
construct!(tag, value).adjacent().map(|pair| pair.1).optional().catch()
}

fn scripts_dir() -> impl Parser<PathBuf> {
let tag = any::<String>("-compile")
.help("Compile scripts to blob")
.guard(|s| s == "-compile", "not compile");
let tag = literal("-compile").help("Compile scripts to blob");
let value = positional::<PathBuf>("SCRIPT_PATH").guard(is_not_slong, "starts with -");
construct!(tag, value).adjacent().map(|pair| pair.1)
}

fn script_paths_file() -> impl Parser<Option<PathBuf>> {
let tag = any::<String>("-compilePathsFile")
.help("File containing a newline-delimited list of redscript paths to compile")
.guard(|s| s == "-compilePathsFile", "not compilePathsFile");
let value = positional::<PathBuf>("SCRIPT_PATHS_FILE").guard(is_not_slong, "starts with -");
construct!(tag, value)
.adjacent()
let tag = literal("-compilePathsFile")
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
.help("File containing a newline-delimited list of redscript paths to compile");
let value = positional::<PathBuf>("SCRIPT_PATHS_FILE").guard(is_not_slong, "starts with -");
construct!(tag, value).adjacent().map(|pair| pair.1).optional().catch()
}

fn cache_dir() -> impl Parser<Option<PathBuf>> {
let tag = any::<String>("-customCacheDir")
.help("A custom cache dir to write the final.redscripts to")
.guard(|s| s == "-customCacheDir", "not customCacheDir");
let value = positional::<PathBuf>("CACHE_DIR").guard(is_not_slong, "starts with -");
construct!(tag, value)
.adjacent()
let tag = literal("-customCacheDir")
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
.help("A custom cache dir to write the final.redscripts to");
let value = positional::<PathBuf>("CACHE_DIR").guard(is_not_slong, "starts with -");
construct!(tag, value).adjacent().map(|pair| pair.1).optional().catch()
}

fn output_cache_file() -> impl Parser<Option<PathBuf>> {
let tag = any::<String>("-outputCacheFile")
.help("A custom output path to write the final.redscripts to")
.guard(|s| s == "-outputCacheFile", "not outputCacheFile");
let value = positional::<PathBuf>("OUTPUT_CACHE_FILE").guard(is_not_slong, "starts with -");
construct!(tag, value)
.adjacent()
let tag = literal("-outputCacheFile")
.anywhere()
.map(|pair| pair.1)
.optional()
.catch()
.help("A custom output path to write the final.redscripts to");
let value = positional::<PathBuf>("OUTPUT_CACHE_FILE").guard(is_not_slong, "starts with -");
construct!(tag, value).adjacent().map(|pair| pair.1).optional().catch()
}

impl Opts {
Expand Down Expand Up @@ -205,24 +172,23 @@ impl Opts {
});
let cache_file = positional::<PathBuf>("CACHE_FILE")
.guard(is_not_slong, "starts with -")
.anywhere()
.optional()
.catch();

let parser = construct!(Opts {
scripts_dir(),
cache_dir(),
optimize,
threads,
warnings,
no_testonly,
no_breakpoint,
profile,
no_exec,
no_debug,
threads,
script_paths_file(),
output_cache_file(),
cache_file,
no_exec,
no_debug
cache_dir(),
scripts_dir(),
cache_file
});
parser.to_options()
}
Expand Down Expand Up @@ -339,13 +305,13 @@ mod test {

#[test]
fn get_help() {
assert_eq!(
"not compile",
Opts::get_parser()
.run_inner(bpaf::Args::from(&["--help"]))
.unwrap_err()
.unwrap_stderr()
);
let help = Opts::get_parser()
.run_inner(bpaf::Args::from(&["--help"]))
.unwrap_err()
.unwrap_stdout();
assert!(help.contains("-compile"));
assert!(help.contains("-customCacheDir"));
assert!(help.contains("-threads"));
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion scc/lib-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pretty_assertions = "1"
assert_fs = "1"

[build-dependencies]
bindgen = "0.71"
bindgen = "0.73"

[lints]
workspace = true
2 changes: 1 addition & 1 deletion scc/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ hashbrown = { workspace = true, features = ["serde"] }
byteorder.workspace = true
flexi_logger.workspace = true
serde = { version = "1", features = ["derive"] }
toml = "0.8"
toml = "1.1"
vmap = { version = "0.6", default-features = false, optional = true }
normpath = "1"
fd-lock = "4"
Expand Down
66 changes: 66 additions & 0 deletions scc/lib/src/hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,69 @@ impl UserHints {
})
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn loads_and_matches_user_hint_files() {
let dir = assert_fs::TempDir::new().unwrap();
fs::write(
dir.path().join("example.toml"),
r#"
[[UNRESOLVED_REF]]
id = "example"
message = "Install the required mod — проверка"
file = "example.reds"
span_starts_with = "MissingType"

[[UNRESOLVED_REF]]
id = "line-match"
message = "Check this declaration"
line_contains = "OtherType"
"#,
)
.unwrap();
let hints = UserHints::load(dir.path()).unwrap();
let hint = hints
.get_by_error(
"UNRESOLVED_REF",
Some(Path::new("example.reds")),
"MissingType value",
"",
)
.unwrap();
assert_eq!(hint.id(), "example");
assert_eq!(hint.message(), "Install the required mod — проверка");
assert!(
hints
.get_by_error("UNRESOLVED_REF", Some(Path::new("other.reds")), "MissingType value", "")
.is_none()
);
assert_eq!(
hints
.get_by_error("UNRESOLVED_REF", None, "", "let value: OtherType;")
.unwrap()
.id(),
"line-match"
);
assert!(
hints
.get_by_error("OTHER_ERROR", None, "MissingType", "OtherType")
.is_none()
);
}

#[test]
fn rejects_malformed_hint_files_with_context() {
let dir = assert_fs::TempDir::new().unwrap();
fs::write(dir.path().join("broken.toml"), "[[UNRESOLVED_REF]\n").unwrap();
assert!(
UserHints::load(dir.path())
.unwrap_err()
.to_string()
.contains("Failed to parse a hint file")
);
}
}
12 changes: 8 additions & 4 deletions scc/lib/tests/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ use scc_lib::timestamp::*;
#[test]
fn no_args() -> Result<(), Box<dyn std::error::Error>> {
let mut cmd = Command::cargo_bin("scc")?;
cmd.assert().failure().stderr(predicate::str::contains(
"Expected <-compile>, pass --help for usage information",
));
cmd.assert()
.failure()
.stderr(predicate::str::contains("-compile"))
.stderr(predicate::str::contains("--help"));
Ok(())
}

#[test]
fn help() -> Result<(), Box<dyn std::error::Error>> {
let mut cmd = Command::cargo_bin("scc")?;
cmd.arg("--help");
cmd.assert().failure().stderr(predicate::str::contains("not compile\n"));
cmd.assert()
.success()
.stdout(predicate::str::contains("-compile"))
.stdout(predicate::str::contains("-customCacheDir"));
Ok(())
}

Expand Down