From 770635ee1c3663ad984a3fc94ea98d252d5d2a6e Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Wed, 25 Feb 2026 00:16:35 -0800 Subject: [PATCH 1/3] Parameterize functions tests with rstest --- tests/functions.rs | 590 +++++++-------------------------------------- tests/lib.rs | 1 + 2 files changed, 82 insertions(+), 509 deletions(-) diff --git a/tests/functions.rs b/tests/functions.rs index c7d0bf751c..4d55115e7e 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -1,71 +1,31 @@ use super::*; -#[test] -fn test_os_arch_functions_in_interpolation() { - Test::new() - .justfile( - r" -foo: - echo {{arch()}} {{os()}} {{os_family()}} {{num_cpus()}} -", - ) - .stdout( - format!( - "{} {} {} {}\n", - target::arch(), - target::os(), - target::family(), - num_cpus::get() - ) - .as_str(), - ) - .stderr( - format!( - "echo {} {} {} {}\n", - target::arch(), - target::os(), - target::family(), - num_cpus::get() - ) - .as_str(), - ) - .success(); -} +#[rstest] +#[case::interpolation("foo:\n echo {{arch()}} {{os()}} {{os_family()}} {{num_cpus()}}")] +#[case::expression("a := arch()\no := os()\nf := os_family()\nn := num_cpus()\n\nfoo:\n echo {{a}} {{o}} {{f}} {{n}}")] +#[case::default_parameter( + "foo a=arch() o=os() f=os_family() n=num_cpus():\n echo {{a}} {{o}} {{f}} {{n}}" +)] +fn test_os_arch_functions(#[case] justfile: &str) { + let expected_stdout = format!( + "{} {} {} {}\n", + target::arch(), + target::os(), + target::family(), + num_cpus::get() + ); + let expected_stderr = format!( + "echo {} {} {} {}\n", + target::arch(), + target::os(), + target::family(), + num_cpus::get() + ); -#[test] -fn test_os_arch_functions_in_expression() { Test::new() - .justfile( - r" -a := arch() -o := os() -f := os_family() -n := num_cpus() - -foo: - echo {{a}} {{o}} {{f}} {{n}} -", - ) - .stdout( - format!( - "{} {} {} {}\n", - target::arch(), - target::os(), - target::family(), - num_cpus::get() - ) - .as_str(), - ) - .stderr( - format!( - "echo {} {} {} {}\n", - target::arch(), - target::os(), - target::family(), - num_cpus::get() - ) - .as_str(), - ) + .justfile(justfile) + .stdout(expected_stdout.as_str()) + .stderr(expected_stderr.as_str()) .success(); } @@ -143,202 +103,34 @@ foo: .success(); } -#[test] -fn broken_without_extension_function() { +#[rstest] +#[case::without_extension_empty("without_extension", "", "Could not extract parent from ``")] +#[case::extension_empty("extension", "", "Could not extract extension from ``")] +#[case::extension_no_ext("extension", "foo", "Could not extract extension from `foo`")] +#[case::file_stem_empty("file_stem", "", "Could not extract file stem from ``")] +#[case::file_name_empty("file_name", "", "Could not extract file name from ``")] +#[case::parent_directory_empty( + "parent_directory", + "", + "Could not extract parent directory from ``" +)] +#[case::parent_directory_root( + "parent_directory", + "/", + "Could not extract parent directory from `/`" +)] +fn broken_path_functions(#[case] func: &str, #[case] input: &str, #[case] error_msg: &str) { if cfg!(windows) { return; } - Test::new() - .justfile( - r" -we := without_extension('') - -foo: - /usr/bin/env echo '{{we}}' -", - ) - .stderr( - format!( - "{} {}\n{}\n{}\n{}\n{}\n", - "error: Call to function `without_extension` failed:", - "Could not extract parent from ``", - " ——▶ justfile:1:8", - " │", - "1 │ we := without_extension(\'\')", - " │ ^^^^^^^^^^^^^^^^^" - ) - .as_str(), - ) - .failure(); -} - -#[test] -fn broken_extension_function() { - if cfg!(windows) { - return; - } - Test::new() - .justfile( - r" -we := extension('') - -foo: - /usr/bin/env echo '{{we}}' -", - ) - .stderr( - format!( - "{}\n{}\n{}\n{}\n{}\n", - "error: Call to function `extension` failed: Could not extract extension from ``", - " ——▶ justfile:1:8", - " │", - "1 │ we := extension(\'\')", - " │ ^^^^^^^^^" - ) - .as_str(), - ) - .failure(); -} - -#[test] -fn broken_extension_function2() { - if cfg!(windows) { - return; - } - Test::new() - .justfile( - r" -we := extension('foo') - -foo: - /usr/bin/env echo '{{we}}' -", - ) - .stderr( - format!( - "{}\n{}\n{}\n{}\n{}\n", - "error: Call to function `extension` failed: Could not extract extension from `foo`", - " ——▶ justfile:1:8", - " │", - "1 │ we := extension(\'foo\')", - " │ ^^^^^^^^^" - ) - .as_str(), - ) - .failure(); -} - -#[test] -fn broken_file_stem_function() { - if cfg!(windows) { - return; - } - Test::new() - .justfile( - r" -we := file_stem('') - -foo: - /usr/bin/env echo '{{we}}' -", - ) - .stderr( - format!( - "{}\n{}\n{}\n{}\n{}\n", - "error: Call to function `file_stem` failed: Could not extract file stem from ``", - " ——▶ justfile:1:8", - " │", - "1 │ we := file_stem(\'\')", - " │ ^^^^^^^^^" - ) - .as_str(), - ) - .failure(); -} - -#[test] -fn broken_file_name_function() { - if cfg!(windows) { - return; - } - Test::new() - .justfile( - r" -we := file_name('') - -foo: - /usr/bin/env echo '{{we}}' -", - ) - .stderr( - format!( - "{}\n{}\n{}\n{}\n{}\n", - "error: Call to function `file_name` failed: Could not extract file name from ``", - " ——▶ justfile:1:8", - " │", - "1 │ we := file_name(\'\')", - " │ ^^^^^^^^^" - ) - .as_str(), - ) - .failure(); -} - -#[test] -fn broken_directory_function() { - if cfg!(windows) { - return; - } - Test::new() - .justfile( - r" -we := parent_directory('') - -foo: - /usr/bin/env echo '{{we}}' -", - ) - .stderr( - format!( - "{} {}\n{}\n{}\n{}\n{}\n", - "error: Call to function `parent_directory` failed:", - "Could not extract parent directory from ``", - " ——▶ justfile:1:8", - " │", - "1 │ we := parent_directory(\'\')", - " │ ^^^^^^^^^^^^^^^^" - ) - .as_str(), - ) - .failure(); -} - -#[test] -fn broken_directory_function2() { - if cfg!(windows) { - return; - } - Test::new() - .justfile( - r" -we := parent_directory('/') - -foo: - /usr/bin/env echo '{{we}}' -", - ) - .stderr( - format!( - "{} {}\n{}\n{}\n{}\n{}\n", - "error: Call to function `parent_directory` failed:", - "Could not extract parent directory from `/`", - " ——▶ justfile:1:8", - " │", - "1 │ we := parent_directory(\'/\')", - " │ ^^^^^^^^^^^^^^^^" - ) - .as_str(), - ) + let carets = "^".repeat(func.len()); + Test::new() + .justfile(format!( + "we := {func}('{input}')\n\nfoo:\n /usr/bin/env echo '{{{{we}}}}'" + )) + .stderr(format!( + "error: Call to function `{func}` failed: {error_msg}\n ——▶ justfile:1:8\n │\n1 │ we := {func}('{input}')\n │ {carets}\n" + )) .failure(); } @@ -399,218 +191,38 @@ fn test_just_executable_function() { .success(); } -#[test] -fn test_os_arch_functions_in_default() { - Test::new() - .justfile( - r" -foo a=arch() o=os() f=os_family() n=num_cpus(): - echo {{a}} {{o}} {{f}} {{n}} -", - ) - .stdout( - format!( - "{} {} {} {}\n", - target::arch(), - target::os(), - target::family(), - num_cpus::get() - ) - .as_str(), - ) - .stderr( - format!( - "echo {} {} {} {}\n", - target::arch(), - target::os(), - target::family(), - num_cpus::get() - ) - .as_str(), - ) - .success(); -} - -#[test] -fn clean() { - Test::new() - .justfile( - " - foo: - echo {{ clean('a/../b') }} - ", - ) - .stdout("b\n") - .stderr("echo b\n") - .success(); -} - -#[test] -fn uppercase() { - Test::new() - .justfile( - " - foo: - echo {{ uppercase('bar') }} - ", - ) - .stdout("BAR\n") - .stderr("echo BAR\n") - .success(); -} - -#[test] -fn lowercase() { - Test::new() - .justfile( - " - foo: - echo {{ lowercase('BAR') }} - ", - ) - .stdout("bar\n") - .stderr("echo bar\n") - .success(); -} - -#[test] -fn uppercamelcase() { - Test::new() - .justfile( - " - foo: - echo {{ uppercamelcase('foo bar') }} - ", - ) - .stdout("FooBar\n") - .stderr("echo FooBar\n") - .success(); -} - -#[test] -fn lowercamelcase() { - Test::new() - .justfile( - " - foo: - echo {{ lowercamelcase('foo bar') }} - ", - ) - .stdout("fooBar\n") - .stderr("echo fooBar\n") - .success(); -} - -#[test] -fn snakecase() { - Test::new() - .justfile( - " - foo: - echo {{ snakecase('foo bar') }} - ", - ) - .stdout("foo_bar\n") - .stderr("echo foo_bar\n") - .success(); -} - -#[test] -fn kebabcase() { - Test::new() - .justfile( - " - foo: - echo {{ kebabcase('foo bar') }} - ", - ) - .stdout("foo-bar\n") - .stderr("echo foo-bar\n") - .success(); -} - -#[test] -fn shoutysnakecase() { - Test::new() - .justfile( - " - foo: - echo {{ shoutysnakecase('foo bar') }} - ", - ) - .stdout("FOO_BAR\n") - .stderr("echo FOO_BAR\n") - .success(); -} - -#[test] -fn titlecase() { - Test::new() - .justfile( - " - foo: - echo {{ titlecase('foo bar') }} - ", - ) - .stdout("Foo Bar\n") - .stderr("echo Foo Bar\n") - .success(); -} - -#[test] -fn shoutykebabcase() { - Test::new() - .justfile( - " - foo: - echo {{ shoutykebabcase('foo bar') }} - ", - ) - .stdout("FOO-BAR\n") - .stderr("echo FOO-BAR\n") - .success(); -} - -#[test] -fn trim() { - Test::new() - .justfile( - " - foo: - echo {{ trim(' bar ') }} - ", - ) - .stdout("bar\n") - .stderr("echo bar\n") +#[rstest] +#[case::clean("clean", "a/../b", "b")] +#[case::uppercase("uppercase", "bar", "BAR")] +#[case::lowercase("lowercase", "BAR", "bar")] +#[case::uppercamelcase("uppercamelcase", "foo bar", "FooBar")] +#[case::lowercamelcase("lowercamelcase", "foo bar", "fooBar")] +#[case::snakecase("snakecase", "foo bar", "foo_bar")] +#[case::kebabcase("kebabcase", "foo bar", "foo-bar")] +#[case::shoutysnakecase("shoutysnakecase", "foo bar", "FOO_BAR")] +#[case::titlecase("titlecase", "foo bar", "Foo Bar")] +#[case::shoutykebabcase("shoutykebabcase", "foo bar", "FOO-BAR")] +#[case::trim("trim", " bar ", "bar")] +#[case::capitalize("capitalize", "BAR", "Bar")] +fn string_functions(#[case] func: &str, #[case] input: &str, #[case] expected: &str) { + Test::new() + .justfile(format!("foo:\n echo {{{{ {func}('{input}') }}}}")) + .stdout(format!("{expected}\n")) + .stderr(format!("echo {expected}\n")) .success(); } #[test] fn replace() { - Test::new() - .justfile( - " - foo: - echo {{ replace('barbarbar', 'bar', 'foo') }} - ", - ) - .stdout("foofoofoo\n") - .stderr("echo foofoofoo\n") - .success(); + assert_eval_eq("replace('barbarbar', 'bar', 'foo')", "foofoofoo"); } #[test] fn replace_regex() { - Test::new() - .justfile( - " - foo: - echo {{ replace_regex('123bar123bar123bar', '\\d+bar', 'foo') }} - ", - ) - .stdout("foofoofoo\n") - .stderr("echo foofoofoo\n") - .success(); + assert_eval_eq( + r"replace_regex('123bar123bar123bar', '\d+bar', 'foo')", + "foofoofoo", + ); } #[test] @@ -636,20 +248,6 @@ error: incomplete escape sequence, reached end of pattern prematurely .failure(); } -#[test] -fn capitalize() { - Test::new() - .justfile( - " - foo: - echo {{ capitalize('BAR') }} - ", - ) - .stdout("Bar\n") - .stderr("echo Bar\n") - .success(); -} - #[test] fn semver_matches() { Test::new() @@ -1409,16 +1007,16 @@ bar: .success(); } -#[test] -fn style_command_default() { +#[rstest] +#[case::command("command", "\x1b[1m")] +#[case::error("error", "\x1b[1;31m")] +#[case::warning("warning", "\x1b[1;33m")] +fn style_functions(#[case] style: &str, #[case] escape_code: &str) { Test::new() - .justfile( - r#" - foo: - @echo '{{ style("command") }}foo{{NORMAL}}' - "#, - ) - .stdout("\x1b[1mfoo\x1b[0m\n") + .justfile(format!( + "foo:\n @echo '{{{{ style(\"{style}\") }}}}foo{{{{NORMAL}}}}'" + )) + .stdout(format!("{escape_code}foo\x1b[0m\n")) .success(); } @@ -1436,32 +1034,6 @@ fn style_command_non_default() { .success(); } -#[test] -fn style_error() { - Test::new() - .justfile( - r#" - foo: - @echo '{{ style("error") }}foo{{NORMAL}}' - "#, - ) - .stdout("\x1b[1;31mfoo\x1b[0m\n") - .success(); -} - -#[test] -fn style_warning() { - Test::new() - .justfile( - r#" - foo: - @echo '{{ style("warning") }}foo{{NORMAL}}' - "#, - ) - .stdout("\x1b[1;33mfoo\x1b[0m\n") - .success(); -} - #[test] fn style_unknown() { Test::new() diff --git a/tests/lib.rs b/tests/lib.rs index 87715b41af..82e1de7c07 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -8,6 +8,7 @@ use { just::{unindent, Response}, pretty_assertions::Comparison, regex::Regex, + rstest::rstest, serde::{Deserialize, Serialize}, serde_json::{json, Value}, std::{ From b09f688ffa2e413b68f916e1bcc59b246bcd21ec Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Wed, 25 Feb 2026 00:04:14 -0800 Subject: [PATCH 2/3] test refactor plan --- TEST_REFACTOR.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 TEST_REFACTOR.md diff --git a/TEST_REFACTOR.md b/TEST_REFACTOR.md new file mode 100644 index 0000000000..1fc1303794 --- /dev/null +++ b/TEST_REFACTOR.md @@ -0,0 +1,79 @@ +# Test Refactoring Plan + +## Goal + +Reduce the number of separate tests and verbosity while still exercising the code in the same way. + +## Strategies + +### 1. Parameterized / data-driven tests (biggest win) + +Many tests differ only in input/output data. Use loop-in-a-single-test: + +```rust +#[test] +fn string_functions() { + for (func, input, expected) in [ + ("uppercase", "bar", "BAR"), + ("lowercase", "BAR", "bar"), + // ... + ] { + Test::new() + .justfile(&format!("foo:\n echo {{{{ {func}('{input}') }}}}")) + .stdout(&format!("{expected}\n")) + .stderr(&format!("echo {expected}\n")) + .success(); + } +} +``` + +Applies to: +- String function tests in `functions.rs` (~20+ tests → 1-2) +- Path function error tests in `functions.rs` (~6+ tests → 1) +- Case conversion tests throughout + +### 2. Merge tests that exercise the same feature with slight variations + +Tests like `test_os_arch_functions_in_interpolation`, `_in_expression`, and `_in_default` repeat identical stdout/stderr assertions — only the justfile differs. These could be a single test with a loop over justfile variants. + +Similarly in `options.rs`, tests sharing the `LONG_SHORT` constant justfile could be one test that runs multiple arg combinations. + +### 3. Macro-generated test functions (preserves individual test names) + +For cases where individual test names are important: + +```rust +macro_rules! test_function { + ($name:ident, $func:expr, $input:expr, $expected:expr) => { + #[test] + fn $name() { + Test::new() + .justfile(&format!("foo:\n echo {{{{ {}('{}') }}}}", $func, $input)) + .stdout(&format!("{}\n", $expected)) + .stderr(&format!("echo {}\n", $expected)) + .success(); + } + }; +} + +test_function!(uppercase, "uppercase", "bar", "BAR"); +test_function!(lowercase, "lowercase", "BAR", "bar"); +``` + +### 4. Consolidate multi-assertion unit tests + +Group related assertions into fewer test functions, similar to what the inline `#[cfg(test)]` unit tests already do. + +### 5. Extract shared error-format helpers + +The "broken function" error tests construct error messages with the same format. A helper function would cut significant duplication. + +## Priority Areas + +| Area | Current tests | Could become | Technique | +|------|--------------|-------------|-----------| +| String functions (`functions.rs`) | ~20 | 1-2 | Data-driven loop | +| Path error tests (`functions.rs`) | ~6 | 1 | Data-driven loop | +| OS/arch placement tests | 3 | 1 | Loop over justfile variants | +| Options arg-passing tests | ~10 | 2-3 | Merge by shared justfile | +| `misc.rs` listing variants | many groups of 3-5 | fewer | Merge closely related ones | From 621b38ef76e170001e8686220dd9a60a48046154 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Wed, 25 Feb 2026 00:12:20 -0800 Subject: [PATCH 3/3] Add rstest --- Cargo.lock | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + 2 files changed, 176 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 40179f8c0c..8f8c7e16eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -405,6 +405,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -427,6 +433,48 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "generate-book" version = "0.0.0" @@ -477,6 +525,18 @@ dependencies = [ "wasip2", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "heck" version = "0.5.0" @@ -513,6 +573,16 @@ dependencies = [ "cc", ] +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_executable" version = "1.0.5" @@ -570,6 +640,7 @@ dependencies = [ "pretty_assertions", "rand", "regex", + "rstest", "rustversion", "semver", "serde", @@ -707,6 +778,12 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -726,6 +803,15 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.104" @@ -850,12 +936,56 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "roff" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", +] + +[[package]] +name = "rstest_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn", + "unicode-ident", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.3" @@ -960,6 +1090,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "snafu" version = "0.8.9" @@ -1077,6 +1213,36 @@ dependencies = [ "syn", ] +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] + [[package]] name = "typed-arena" version = "2.0.2" @@ -1378,6 +1544,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "winsafe" version = "0.0.19" diff --git a/Cargo.toml b/Cargo.toml index 545011120d..0106f5a44e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ ctrlc = { version = "3.1.1", features = ["termination"] } [dev-dependencies] clap_complete = "=4.5.48" pretty_assertions = "1.0.0" +rstest = "0.26.1" temptree = "0.2.0" which = "8.0.0"