mirror of
https://github.com/eza-community/eza
synced 2024-09-30 05:06:30 +00:00
feat(input): adding piping of args
fix: lifetime issue fix: try to fix lifetime issues for pipe from stdin
This commit is contained in:
parent
2a63c93a63
commit
0889f23919
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -36,3 +36,6 @@ tests/tmp
|
|||
|
||||
## Dynamically generated
|
||||
tests/test_dir
|
||||
|
||||
# Miscenallous
|
||||
.idea
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 file’s 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
|
||||
=============
|
||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -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 won’t 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);
|
||||
|
|
|
@ -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,
|
||||
]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
29
src/options/stdin.rs
Normal 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
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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>;
|
||||
|
|
Loading…
Reference in a new issue