Skip to content
Merged
6 changes: 6 additions & 0 deletions src/uu/mv/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,9 @@ mv-prompt-overwrite-mode = replace {$target}, overriding mode {$mode_info}?
# Progress messages
mv-progress-moving = moving
mv-error-dest-appeared = destination { $path } was replaced while moving; refusing to follow it

# Hardlink preservation errors
mv-error-hardlink-io = I/O error during hardlink operation: { $error }
mv-error-hardlink-scan = failed to scan files for hardlinks: { $error }
mv-error-hardlink-preserve = failed to preserve hardlink: { $source } -> { $target }
mv-error-hardlink-metadata = metadata access error for { $path }: { $error }
6 changes: 6 additions & 0 deletions src/uu/mv/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,9 @@ mv-prompt-overwrite = écraser {$target} ?
# Messages de progression
mv-progress-moving = déplacement
mv-error-dest-appeared = la destination { $path } a été remplacée pendant le déplacement ; refus de la suivre

# Erreurs de préservation des liens physiques
mv-error-hardlink-io = erreur d'E/S lors de l'opération sur le lien physique : { $error }
mv-error-hardlink-scan = échec de l'analyse des fichiers pour les liens physiques : { $error }
mv-error-hardlink-preserve = échec de la préservation du lien physique : { $source } -> { $target }
mv-error-hardlink-metadata = erreur d'accès aux métadonnées de { $path } : { $error }
69 changes: 17 additions & 52 deletions src/uu/mv/src/hardlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::io::{self, Write as _};
use std::path::{Path, PathBuf};

use uucore::display::Quotable;
use uucore::translate;

/// Tracks hardlinks during cross-partition moves to preserve them
#[derive(Debug, Default)]
Expand Down Expand Up @@ -42,67 +43,31 @@ pub struct HardlinkOptions {
}

/// Errors that can occur during hardlink operations
#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
pub enum HardlinkError {
Io(io::Error),
#[error("{}", translate!("mv-error-hardlink-io", "error" => .0))]
Io(#[from] io::Error),
#[error("{}", translate!("mv-error-hardlink-scan", "error" => .0))]
Scan(String),
Preservation { source: PathBuf, target: PathBuf },
Metadata { path: PathBuf, error: io::Error },
}

impl std::fmt::Display for HardlinkError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "I/O error during hardlink operation: {e}"),
Self::Scan(msg) => {
write!(f, "Failed to scan files for hardlinks: {msg}")
}
Self::Preservation { source, target } => {
write!(
f,
"Failed to preserve hardlink: {} -> {}",
source.quote(),
target.quote()
)
}
Self::Metadata { path, error } => {
write!(f, "Metadata access error for {}: {error}", path.quote())
}
}
}
}

impl std::error::Error for HardlinkError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Metadata { error, .. } => Some(error),
_ => None,
}
}
}

impl From<io::Error> for HardlinkError {
fn from(error: io::Error) -> Self {
Self::Io(error)
}
#[error("{}", translate!("mv-error-hardlink-preserve", "source" => .source_path.quote(), "target" => .target_path.quote()))]
Preservation {
source_path: PathBuf,
target_path: PathBuf,
},
#[error("{}", translate!("mv-error-hardlink-metadata", "path" => .path.quote(), "error" => .error))]
Metadata {
path: PathBuf,
#[source]
error: io::Error,
},
}

impl From<HardlinkError> for io::Error {
fn from(error: HardlinkError) -> Self {
match error {
HardlinkError::Io(e) => e,
HardlinkError::Scan(msg) => Self::other(msg),
HardlinkError::Preservation { source, target } => Self::other(format!(
"Failed to preserve hardlink: {} -> {}",
source.quote(),
target.quote()
)),

HardlinkError::Metadata { path, error } => Self::other(format!(
"Metadata access error for {}: {error}",
path.quote(),
)),
other => Self::other(other.to_string()),
}
}
}
Expand Down
12 changes: 2 additions & 10 deletions src/uu/split/src/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,14 @@
//! [radix]: https://en.wikipedia.org/wiki/Radix
//! [positional notation]: https://en.wikipedia.org/wiki/Positional_notation

use std::error::Error;
use std::fmt::{self, Display, Formatter, Write};
use uucore::translate;

/// An overflow due to incrementing a number beyond its representable limit.
#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
#[error("{}", translate!("split-error-overflow"))]
pub struct Overflow;

impl Display for Overflow {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", translate!("split-error-overflow"))
}
}

impl Error for Overflow {}

/// A number in arbitrary radix expressed in a positional notation.
///
/// Use the [`Number`] enum to represent an arbitrary number in an
Expand Down
12 changes: 2 additions & 10 deletions src/uu/tsort/src/tsort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use rustc_hash::FxHashMap;
use std::collections::VecDeque;
use std::collections::hash_map::Entry;
use std::ffi::OsString;
use std::fmt;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
Expand Down Expand Up @@ -136,17 +135,10 @@ pub fn uu_app() -> Command {
//
// Diagnostics go through Display, so invalid UTF-8 bytes are represented
// lossily here.
#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
#[error("{}", String::from_utf8_lossy(.0))]
struct LoopNode<'a>(&'a [u8]);

impl fmt::Display for LoopNode<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(self.0))
}
}

impl std::error::Error for LoopNode<'_> {}

impl UError for Error {}
impl UError for LoopNode<'_> {}

Expand Down
8 changes: 8 additions & 0 deletions src/uucore/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,11 @@ perms-cannot-access-replaced = cannot access { $file }: replaced while it was be
# Encoding/decoding error messages (base32, base64, basenc)
encoding-error-invalid-input = invalid input
encoding-error-invalid-input-z85-length = invalid input (length must be multiple of 4 characters)

# Backup control messages (uucore::backup_control)
backup-error-invalid-argument = invalid argument { $arg } for '{ $origin }'
backup-error-ambiguous-argument = ambiguous argument { $arg } for '{ $origin }'
backup-valid-arguments = Valid arguments are:

# OsStr conversion errors
error-non-utf8-os-str = invalid UTF-8 input { $input } encountered when converting to bytes on a platform that doesn't expose byte arguments
8 changes: 8 additions & 0 deletions src/uucore/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,11 @@ mode-diag-help-syntax = un mode est soit octal, comme 644, soit des clauses comm
# Messages d'erreur d'encodage/décodage (base32, base64, basenc)
encoding-error-invalid-input = entrée invalide
encoding-error-invalid-input-z85-length = entrée invalide (la longueur doit être un multiple de 4 caractères)

# Messages de contrôle des sauvegardes (uucore::backup_control)
backup-error-invalid-argument = argument { $arg } invalide pour '{ $origin }'
backup-error-ambiguous-argument = argument { $arg } ambigu pour '{ $origin }'
backup-valid-arguments = Les arguments valides sont :

# Erreurs de conversion OsStr
error-non-utf8-os-str = entrée UTF-8 invalide { $input } rencontrée lors de la conversion en octets sur une plateforme qui n'expose pas les arguments en octets
53 changes: 29 additions & 24 deletions src/uucore/src/lib/features/backup_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@
use crate::display::Quotable;
use crate::error::{UError, UResult};
use crate::fs::FileInformation;
use crate::translate;
use clap::ArgMatches;
use std::error::Error;
use std::ffi::{OsStr, OsString};
use std::fmt::{Debug, Display};
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::{env, fs};

Expand All @@ -106,8 +106,8 @@ the VERSION_CONTROL environment variable. Here are the values:
existing, nil numbered if numbered backups exist, simple otherwise
simple, never always make simple backups";

static VALID_ARGS_HELP: &str = "Valid arguments are:
- 'none', 'off'
/// The values `--backup` accepts; they are option values, so they are not translated.
static VALID_ARGS_LIST: &str = " - 'none', 'off'
- 'simple', 'never'
- 'existing', 'nil'
- 'numbered', 't'";
Expand Down Expand Up @@ -135,15 +135,25 @@ pub enum BackupMode {
///
/// Errors are currently raised by [`determine_backup_mode`] only. All errors
/// are implemented as [`UError`] for uniform handling across utilities.
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum BackupError {
/// An invalid argument (e.g. 'foo') was given as backup type. First
/// parameter is the argument, second is the arguments origin (CLI or
/// ENV-var)
#[error(
"{}\n{}\n{VALID_ARGS_LIST}",
translate!("backup-error-invalid-argument", "arg" => .0.quote(), "origin" => .1),
translate!("backup-valid-arguments")
)]
InvalidArgument(String, String),
/// An ambiguous argument (e.g. 'n') was given as backup type. First
/// parameter is the argument, second is the arguments origin (CLI or
/// ENV-var)
#[error(
"{}\n{}\n{VALID_ARGS_LIST}",
translate!("backup-error-ambiguous-argument", "arg" => .0.quote(), "origin" => .1),
translate!("backup-valid-arguments")
)]
AmbiguousArgument(String, String),
}

Expand All @@ -153,25 +163,6 @@ impl UError for BackupError {
}
}

impl Error for BackupError {}

impl Display for BackupError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidArgument(arg, origin) => write!(
f,
"invalid argument {} for '{origin}'\n{VALID_ARGS_HELP}",
arg.quote(),
),
Self::AmbiguousArgument(arg, origin) => write!(
f,
"ambiguous argument {} for '{origin}'\n{VALID_ARGS_HELP}",
arg.quote(),
),
}
}
}

/// Arguments for backup-related functionality.
///
/// Rather than implementing the `clap`-Arguments for every utility, it is
Expand Down Expand Up @@ -538,6 +529,16 @@ mod tests {
use super::*;
use clap::Command;

/// The error messages go through `translate!`, which needs the English
/// strings loaded to give back more than the message key.
fn load_english_messages() {
// SAFETY: test-only; like the uptime tests, it always sets the same value.
unsafe {
env::set_var("LANG", "en_US.UTF-8");
}
let _ = crate::locale::setup_localization("mv");
Comment on lines 531 to +539
}

fn make_app() -> Command {
Command::new("command")
.arg(arguments::backup())
Expand Down Expand Up @@ -596,6 +597,7 @@ mod tests {
// --backup errors on invalid argument
#[test]
fn test_backup_mode_long_with_args_invalid() {
load_english_messages();
let matches = make_app().get_matches_from(vec!["command", "--backup=foobar"]);
let result = determine_backup_mode(None, &matches);
assert!(result.is_err());
Expand All @@ -606,6 +608,7 @@ mod tests {
// --backup errors on ambiguous argument
#[test]
fn test_backup_mode_long_with_args_ambiguous() {
load_english_messages();
let matches = make_app().get_matches_from(vec!["command", "--backup=n"]);
let result = determine_backup_mode(None, &matches);
assert!(result.is_err());
Expand Down Expand Up @@ -640,6 +643,7 @@ mod tests {
// --backup errors on invalid VERSION_CONTROL env var
#[test]
fn test_backup_mode_long_with_env_var_invalid() {
load_english_messages();
let matches = make_app().get_matches_from(vec!["command", "--backup"]);
let result = determine_backup_mode(Some("foobar".into()), &matches);
assert!(result.is_err());
Expand All @@ -650,6 +654,7 @@ mod tests {
// --backup errors on ambiguous VERSION_CONTROL env var
#[test]
fn test_backup_mode_long_with_env_var_ambiguous() {
load_english_messages();
let matches = make_app().get_matches_from(vec!["command", "--backup"]);
let result = determine_backup_mode(Some("n".into()), &matches);
assert!(result.is_err());
Expand Down
Loading
Loading