diff --git a/Cargo.lock b/Cargo.lock index 3de0ea0ccb3..679bc75d07c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,7 +77,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -88,7 +88,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -118,6 +118,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "autocfg" version = "1.5.0" @@ -1001,7 +1007,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1730,7 +1736,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2046,7 +2052,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2645,7 +2651,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2866,7 +2872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2945,7 +2951,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2955,7 +2961,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3706,6 +3712,7 @@ dependencies = [ name = "uu_ln" version = "0.8.0" dependencies = [ + "assert_matches", "clap", "fluent", "thiserror 2.0.18", @@ -4747,7 +4754,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f62ac128787..7c9b7e64641 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -386,6 +386,7 @@ version = "0.8.0" [workspace.dependencies] ansi-width = "0.1.0" +assert_matches = "1.5.0" bigdecimal = "0.4" binary-heap-plus = "0.5.0" bstr = "1.9.1" diff --git a/src/uu/ln/Cargo.toml b/src/uu/ln/Cargo.toml index b8dd3e03965..6af99e3f44f 100644 --- a/src/uu/ln/Cargo.toml +++ b/src/uu/ln/Cargo.toml @@ -25,6 +25,9 @@ uucore = { workspace = true, features = ["backup-control", "fs"] } thiserror = { workspace = true } fluent = { workspace = true } +[dev-dependencies] +assert_matches = { workspace = true } + [[bin]] name = "ln" path = "src/main.rs" diff --git a/src/uu/ln/locales/en-US.ftl b/src/uu/ln/locales/en-US.ftl index 337d5c2a0d9..a28f1212302 100644 --- a/src/uu/ln/locales/en-US.ftl +++ b/src/uu/ln/locales/en-US.ftl @@ -26,6 +26,7 @@ ln-help-verbose = print name of each linked file ln-error-target-is-not-directory = target {$target} is not a directory ln-error-same-file = {$file1} and {$file2} are the same file ln-error-missing-destination = missing destination file operand after {$operand} +ln-error-missing-operand = missing operand ln-error-extra-operand = extra operand {$operand} Try '{$program} --help' for more information. ln-error-could-not-update = Could not update {$target}: {$error} diff --git a/src/uu/ln/locales/fr-FR.ftl b/src/uu/ln/locales/fr-FR.ftl index d8ba0996722..56e72f3e55c 100644 --- a/src/uu/ln/locales/fr-FR.ftl +++ b/src/uu/ln/locales/fr-FR.ftl @@ -27,6 +27,7 @@ ln-help-verbose = afficher le nom de chaque fichier lié ln-error-target-is-not-directory = la cible {$target} n'est pas un répertoire ln-error-same-file = {$file1} et {$file2} sont le même fichier ln-error-missing-destination = opérande de fichier de destination manquant après {$operand} +ln-error-missing-operand = opérande manquant ln-error-extra-operand = opérande supplémentaire {$operand} Essayez « {$program} --help » pour plus d'informations. ln-error-could-not-update = Impossible de mettre à jour {$target} : {$error} diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index 96e708b2b1a..ae44c66b320 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -70,6 +70,9 @@ pub enum LnError { #[error("{}", translate!("ln-error-missing-destination", "operand" => _0.quote()))] MissingDestination(PathBuf), + #[error("{}", translate!("ln-error-missing-operand"))] + MissingOperand, + #[error("{}", translate!("ln-error-extra-operand", "operand" => _0.quote(), "program" => _1.clone()))] ExtraOperand(OsString, String), @@ -270,6 +273,10 @@ pub fn uu_app() -> Command { /// /// This is made public to allow other apps to use `ln` as a library. pub fn exec(files: &[PathBuf], settings: &Settings) -> LnResult<()> { + if files.is_empty() { + return Err(LnError::MissingOperand); + } + // Handle cases where we create links in a directory first. if let Some(ref target_path) = settings.target_dir { // 4th form: a directory is specified by -t. @@ -298,7 +305,6 @@ pub fn exec(files: &[PathBuf], settings: &Settings) -> LnResult<()> { uucore::execution_phrase().to_string(), )); } - assert!(!files.is_empty()); link(&files[0], &files[1], settings) } @@ -530,3 +536,67 @@ fn symlink, P2: AsRef>(_src: P1, _dst: P2) -> io::Result<( "symlinks not supported on this platform", )) } + +#[cfg(test)] +mod tests { + use super::*; + use assert_matches::assert_matches; + use std::ffi::OsString; + + #[test] + fn test_exec_missing_operand() { + let settings: Settings = Settings { + overwrite: OverwriteMode::NoClobber, + backup: BackupMode::None, + suffix: OsString::from(""), + symbolic: false, + relative: false, + logical: false, + target_dir: None, + no_target_dir: false, + no_dereference: false, + verbose: false, + }; + + let result = exec(&[], &settings); + assert_matches!(result, Err(LnError::MissingOperand)); + } + + #[test] + fn test_exec_missing_destination() { + let settings = Settings { + overwrite: OverwriteMode::NoClobber, + backup: BackupMode::None, + suffix: OsString::from(""), + symbolic: false, + relative: false, + logical: false, + target_dir: None, + no_target_dir: true, + no_dereference: false, + verbose: false, + }; + + let result = exec(&["a".into()], &settings); + assert_matches!(result, Err(LnError::MissingDestination(path)) if *path == *"a"); + } + + #[test] + fn test_exec_extra_operand() { + let settings = Settings { + overwrite: OverwriteMode::NoClobber, + backup: BackupMode::None, + suffix: OsString::from(""), + symbolic: false, + relative: false, + logical: false, + target_dir: None, + no_target_dir: true, + no_dereference: false, + verbose: false, + }; + + let result = exec(&["a".into(), "b".into(), "c".into()], &settings); + assert_matches!(result, Err(LnError::ExtraOperand(operand, _)) if operand == "c"); + } +}