diff --git a/src/uu/tee/src/cli.rs b/src/uu/tee/src/cli.rs new file mode 100644 index 00000000000..9ad54405061 --- /dev/null +++ b/src/uu/tee/src/cli.rs @@ -0,0 +1,103 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// spell-checker:ignore nopipe + +use clap::{Arg, ArgAction, Command, builder::PossibleValue}; +use std::ffi::OsString; +use uucore::parser::shortcut_value_parser::ShortcutValueParser; +pub use uucore::{format_usage, translate}; + +pub mod options { + pub const APPEND: &str = "append"; + pub const IGNORE_INTERRUPTS: &str = "ignore-interrupts"; + pub const FILE: &str = "file"; + pub const IGNORE_PIPE_ERRORS: &str = "ignore-pipe-errors"; + pub const OUTPUT_ERROR: &str = "output-error"; +} + +#[derive(Clone, Debug)] +pub enum OutputErrorMode { + /// Diagnose write error on any output + Warn, + /// Diagnose write error on any output that is not a pipe + WarnNoPipe, + /// Exit upon write error on any output + Exit, + /// Exit upon write error on any output that is not a pipe + ExitNoPipe, +} + +#[allow(dead_code)] +pub struct Options { + pub append: bool, + pub ignore_interrupts: bool, + pub ignore_pipe_errors: bool, + pub files: Vec, + pub output_error: Option, +} + +pub fn uu_app() -> Command { + Command::new("tee") + .version(uucore::crate_version!()) + .help_template(uucore::localized_help_template("tee")) + .about(translate!("tee-about")) + .override_usage(format_usage(&translate!("tee-usage"))) + .after_help(translate!("tee-after-help")) + .infer_long_args(true) + // Since we use value-specific help texts for "--output-error", clap's "short help" and "long help" differ. + // However, this is something that the GNU tests explicitly test for, so we *always* show the long help instead. + .disable_help_flag(true) + .arg( + Arg::new("--help") + .short('h') + .long("help") + .help(translate!("tee-help-help")) + .action(ArgAction::HelpLong), + ) + .arg( + Arg::new(options::APPEND) + .long(options::APPEND) + .short('a') + .help(translate!("tee-help-append")) + .action(ArgAction::SetTrue) + .overrides_with(options::APPEND), + ) + .arg( + Arg::new(options::IGNORE_INTERRUPTS) + .long(options::IGNORE_INTERRUPTS) + .short('i') + .help(translate!("tee-help-ignore-interrupts")) + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new(options::FILE) + .action(ArgAction::Append) + .value_hint(clap::ValueHint::FilePath) + .value_parser(clap::value_parser!(OsString)), + ) + .arg( + Arg::new(options::IGNORE_PIPE_ERRORS) + .short('p') + .help(translate!("tee-help-ignore-pipe-errors")) + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new(options::OUTPUT_ERROR) + .long(options::OUTPUT_ERROR) + .require_equals(true) + .num_args(0..=1) + .default_missing_value("warn-nopipe") + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("warn").help(translate!("tee-help-output-error-warn")), + PossibleValue::new("warn-nopipe") + .help(translate!("tee-help-output-error-warn-nopipe")), + PossibleValue::new("exit").help(translate!("tee-help-output-error-exit")), + PossibleValue::new("exit-nopipe") + .help(translate!("tee-help-output-error-exit-nopipe")), + ])) + .help(translate!("tee-help-output-error")), + ) +} diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index da4b921861e..272083b5c41 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -3,53 +3,25 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use clap::{Arg, ArgAction, Command, builder::PossibleValue}; +// spell-checker:ignore espidf nopipe + use std::ffi::OsString; use std::fs::OpenOptions; use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin, stdout}; use std::path::PathBuf; use uucore::display::Quotable; use uucore::error::{UResult, strip_errno}; -use uucore::format_usage; -use uucore::parser::shortcut_value_parser::ShortcutValueParser; use uucore::translate; -// spell-checker:ignore espidf nopipe +mod cli; +pub use crate::cli::uu_app; +use crate::cli::{Options, OutputErrorMode, options}; #[cfg(target_os = "linux")] use uucore::signals::ensure_stdout_not_broken; #[cfg(unix)] use uucore::signals::{disable_pipe_errors, ignore_interrupts}; -mod options { - pub const APPEND: &str = "append"; - pub const IGNORE_INTERRUPTS: &str = "ignore-interrupts"; - pub const FILE: &str = "file"; - pub const IGNORE_PIPE_ERRORS: &str = "ignore-pipe-errors"; - pub const OUTPUT_ERROR: &str = "output-error"; -} - -#[allow(dead_code)] -struct Options { - append: bool, - ignore_interrupts: bool, - ignore_pipe_errors: bool, - files: Vec, - output_error: Option, -} - -#[derive(Clone, Debug)] -enum OutputErrorMode { - /// Diagnose write error on any output - Warn, - /// Diagnose write error on any output that is not a pipe - WarnNoPipe, - /// Exit upon write error on any output - Exit, - /// Exit upon write error on any output that is not a pipe - ExitNoPipe, -} - #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?; @@ -84,69 +56,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { tee(&options).map_err(|_| 1.into()) } -pub fn uu_app() -> Command { - Command::new("tee") - .version(uucore::crate_version!()) - .help_template(uucore::localized_help_template("tee")) - .about(translate!("tee-about")) - .override_usage(format_usage(&translate!("tee-usage"))) - .after_help(translate!("tee-after-help")) - .infer_long_args(true) - // Since we use value-specific help texts for "--output-error", clap's "short help" and "long help" differ. - // However, this is something that the GNU tests explicitly test for, so we *always* show the long help instead. - .disable_help_flag(true) - .arg( - Arg::new("--help") - .short('h') - .long("help") - .help(translate!("tee-help-help")) - .action(ArgAction::HelpLong), - ) - .arg( - Arg::new(options::APPEND) - .long(options::APPEND) - .short('a') - .help(translate!("tee-help-append")) - .action(ArgAction::SetTrue) - .overrides_with(options::APPEND), - ) - .arg( - Arg::new(options::IGNORE_INTERRUPTS) - .long(options::IGNORE_INTERRUPTS) - .short('i') - .help(translate!("tee-help-ignore-interrupts")) - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new(options::FILE) - .action(ArgAction::Append) - .value_hint(clap::ValueHint::FilePath) - .value_parser(clap::value_parser!(OsString)), - ) - .arg( - Arg::new(options::IGNORE_PIPE_ERRORS) - .short('p') - .help(translate!("tee-help-ignore-pipe-errors")) - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new(options::OUTPUT_ERROR) - .long(options::OUTPUT_ERROR) - .require_equals(true) - .num_args(0..=1) - .default_missing_value("warn-nopipe") - .value_parser(ShortcutValueParser::new([ - PossibleValue::new("warn").help(translate!("tee-help-output-error-warn")), - PossibleValue::new("warn-nopipe") - .help(translate!("tee-help-output-error-warn-nopipe")), - PossibleValue::new("exit").help(translate!("tee-help-output-error-exit")), - PossibleValue::new("exit-nopipe") - .help(translate!("tee-help-output-error-exit-nopipe")), - ])) - .help(translate!("tee-help-output-error")), - ) -} - fn tee(options: &Options) -> Result<()> { #[cfg(unix)] {