feat(input): adding piping of args

fix: lifetime issue
fix: try to fix lifetime issues for pipe from stdin
This commit is contained in:
MartinFillon 2023-11-12 00:14:37 +01:00 committed by Christina Sørensen
parent 2a63c93a63
commit 0889f23919
12 changed files with 79 additions and 9 deletions

3
.gitignore vendored
View File

@ -36,3 +36,6 @@ tests/tmp
## Dynamically generated
tests/test_dir
# Miscenallous
.idea

View File

@ -142,6 +142,7 @@ These options are available when running with `--long` (`-l`):
- **--no-filesize**: suppress the filesize field
- **--no-user**: suppress the user field
- **--no-time**: suppress the time field
- **--stdin**: read file names from stdin
Some of the options accept parameters:

View File

@ -110,6 +110,7 @@ complete -c eza -l no-filesize -d "Suppress the filesize field"
complete -c eza -l no-user -d "Suppress the user field"
complete -c eza -l no-time -d "Suppress the time field"
complete -c eza -s M -l mounts -d "Show mount details"
complete -c eza -l stdin -d "Read file names from standard input"
# Optional extras
complete -c eza -l git -d "List each file's Git status, if tracked"

View File

@ -58,4 +58,5 @@ export extern "eza" [
--extended(-@) # List each file's extended attributes and sizes
--context(-Z) # List each file's security context
--smart-group # Only show group if it has a different name from owner
--stdin # Read file paths from stdin
]

View File

@ -67,7 +67,8 @@ __eza() {
{-Z,--context}"[List each file's security context]" \
{-M,--mounts}"[Show mount details (long mode only)]" \
'*:filename:_files' \
--smart-group"[Only show group if it has a different name from owner]"
--smart-group"[Only show group if it has a different name from owner]" \
--stdin"[Read file names from stdin]"
}
__eza

View File

@ -237,6 +237,9 @@ These options are available when running with `--long` (`-l`):
`--no-time`
: Suppress the time field.
`--stdin`
: read file names from stdin, one per line or other separator specified in environment
`-@`, `--extended`
: List each files extended attributes and sizes.
@ -323,6 +326,9 @@ If set, automates the same behavior as using `--icons` or `--icons=auto`. Useful
Any explicit use of the `--icons=WHEN` flag overrides this behavior.
## `EZA_STDIN_SEPARATOR`
Specifies the separator to use when reading file names from stdin. Defaults to newline.
EXIT STATUSES
=============

View File

@ -23,20 +23,20 @@
use std::env;
use std::ffi::{OsStr, OsString};
use std::io::{self, ErrorKind, IsTerminal, Write};
use std::io::{self, stdin, ErrorKind, IsTerminal, Read, Write};
use std::path::{Component, PathBuf};
use std::process::exit;
use ansiterm::{ANSIStrings, Style};
use log::*;
use crate::fs::feature::git::GitCache;
use crate::fs::filter::GitIgnore;
use crate::fs::{Dir, File};
use crate::options::stdin::FilesInput;
use crate::options::{vars, Options, OptionsResult, Vars};
use crate::output::{details, escape, file_name, grid, grid_details, lines, Mode, View};
use crate::theme::Theme;
use log::*;
mod fs;
mod info;
@ -60,13 +60,29 @@ fn main() {
let stdout_istty = io::stdout().is_terminal();
let mut input = String::new();
let args: Vec<_> = env::args_os().skip(1).collect();
match Options::parse(args.iter().map(std::convert::AsRef::as_ref), &LiveVars) {
OptionsResult::Ok(options, mut input_paths) => {
// List the current directory by default.
// (This has to be done here, otherwise git_options wont see it.)
if input_paths.is_empty() {
input_paths = vec![OsStr::new(".")];
match &options.stdin {
FilesInput::Args => {
input_paths = vec![OsStr::new(".")];
}
FilesInput::Stdin(separator) => {
stdin()
.read_to_string(&mut input)
.expect("Failed to read from stdin");
input_paths.extend(
input
.split(&separator.clone().into_string().unwrap_or("\n".to_string()))
.map(std::ffi::OsStr::new).filter(|s| !s.is_empty())
.collect::<Vec<_>>(),
);
}
}
}
let git = git_options(&options, &input_paths);

View File

@ -80,6 +80,7 @@ pub static GIT_REPOS_NO_STAT: Arg = Arg { short: None, long: "git-repos-no
pub static EXTENDED: Arg = Arg { short: Some(b'@'), long: "extended", takes_value: TakesValue::Forbidden };
pub static OCTAL: Arg = Arg { short: Some(b'o'), long: "octal-permissions", takes_value: TakesValue::Forbidden };
pub static SECURITY_CONTEXT: Arg = Arg { short: Some(b'Z'), long: "context", takes_value: TakesValue::Forbidden };
pub static STDIN: Arg = Arg { short: None, long: "stdin", takes_value: TakesValue::Forbidden };
pub static ALL_ARGS: Args = Args(&[
&VERSION, &HELP,
@ -96,5 +97,5 @@ pub static ALL_ARGS: Args = Args(&[
&NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &SMART_GROUP,
&GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT,
&EXTENDED, &OCTAL, &SECURITY_CONTEXT
&EXTENDED, &OCTAL, &SECURITY_CONTEXT, &STDIN,
]);

View File

@ -75,7 +75,8 @@ LONG VIEW OPTIONS
-o, --octal-permissions list each file's permission in octal format
--no-filesize suppress the filesize field
--no-user suppress the user field
--no-time suppress the time field";
--no-time suppress the time field
--stdin read file names from stdin, one per line or other separator specified in environment";
static GIT_VIEW_HELP: &str = " \
--git list each file's Git status, if tracked or ignored

View File

@ -72,6 +72,7 @@ use std::ffi::OsStr;
use crate::fs::dir_action::DirAction;
use crate::fs::filter::{FileFilter, GitIgnore};
use crate::options::stdin::FilesInput;
use crate::output::{details, grid_details, Mode, View};
use crate::theme::Options as ThemeOptions;
@ -95,7 +96,9 @@ use self::parser::MatchedFlags;
pub mod vars;
pub use self::vars::Vars;
pub mod stdin;
mod version;
use self::version::VersionString;
/// These **options** represent a parsed, error-checked versions of the
@ -117,14 +120,17 @@ pub struct Options {
/// The options to make up the styles of the UI and file names.
pub theme: ThemeOptions,
/// Whether to read file names from stdin instead of the command-line
pub stdin: FilesInput,
}
impl Options {
impl<'args> Options {
/// Parse the given iterator of command-line strings into an Options
/// struct and a list of free filenames, using the environment variables
/// for extra options.
#[allow(unused_results)]
pub fn parse<'args, I, V>(args: I, vars: &V) -> OptionsResult<'args>
pub fn parse<I, V>(args: I, vars: &V) -> OptionsResult<'args>
where
I: IntoIterator<Item = &'args OsStr>,
V: Vars,
@ -199,12 +205,14 @@ impl Options {
let dir_action = DirAction::deduce(matches, matches!(view.mode, Mode::Details(_)))?;
let filter = FileFilter::deduce(matches)?;
let theme = ThemeOptions::deduce(matches, vars)?;
let stdin = FilesInput::deduce(matches, vars)?;
Ok(Self {
dir_action,
filter,
view,
theme,
stdin,
})
}
}

29
src/options/stdin.rs Normal file
View File

@ -0,0 +1,29 @@
use crate::options::parser::MatchedFlags;
use crate::options::vars::EZA_STDIN_SEPARATOR;
use crate::options::{flags, OptionsError, Vars};
use std::ffi::OsString;
use std::io;
use std::io::IsTerminal;
#[derive(Debug, PartialEq)]
pub enum FilesInput {
Stdin(OsString),
Args,
}
impl FilesInput {
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
Ok(
if io::stdin().is_terminal() || !matches.has(&flags::STDIN)? {
FilesInput::Args
} else if matches.has(&flags::STDIN)? && !io::stdin().is_terminal() {
let separator = vars
.get(EZA_STDIN_SEPARATOR)
.unwrap_or(OsString::from("\n"));
FilesInput::Stdin(separator)
} else {
FilesInput::Args
},
)
}
}

View File

@ -64,6 +64,8 @@ pub static EZA_MIN_LUMINANCE: &str = "EZA_MIN_LUMINANCE";
/// Any explicit use of `--icons=WHEN` overrides this behavior.
pub static EZA_ICONS_AUTO: &str = "EZA_ICONS_AUTO";
pub static EZA_STDIN_SEPARATOR: &str = "EZA_STDIN_SEPARATOR";
/// Mockable wrapper for `std::env::var_os`.
pub trait Vars {
fn get(&self, name: &'static str) -> Option<OsString>;