mirror of
https://github.com/Llewellynvdm/zoxide.git
synced 2024-11-22 12:55:13 +00:00
Add zoxide query --all flag (#206)
This commit is contained in:
parent
d33bfd111f
commit
ba8a5f3167
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Auto-generated shell completions.
|
- Auto-generated shell completions.
|
||||||
|
- `zoxide query --all` for listing deleted directories.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
7
build.rs
7
build.rs
@ -41,5 +41,12 @@ fn generate_completions() {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let version = git_version().unwrap_or_else(crate_version);
|
let version = git_version().unwrap_or_else(crate_version);
|
||||||
println!("cargo:rustc-env=ZOXIDE_VERSION={}", version);
|
println!("cargo:rustc-env=ZOXIDE_VERSION={}", version);
|
||||||
|
|
||||||
|
// Since we are generating completions in the package directory, we need to
|
||||||
|
// set this so that Cargo doesn't rebuild every time.
|
||||||
|
println!("cargo:rerun-if-changed=src");
|
||||||
|
println!("cargo:rerun-if-changed=templates");
|
||||||
|
println!("cargo:rerun-if-changed=tests");
|
||||||
|
|
||||||
generate_completions();
|
generate_completions();
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ _arguments "${_arguments_options[@]}" \
|
|||||||
(query)
|
(query)
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
'--exclude=[Exclude a path from results]' \
|
'--exclude=[Exclude a path from results]' \
|
||||||
|
'--all[Show deleted directories]' \
|
||||||
'(-l --list)-i[Use interactive selection]' \
|
'(-l --list)-i[Use interactive selection]' \
|
||||||
'(-l --list)--interactive[Use interactive selection]' \
|
'(-l --list)--interactive[Use interactive selection]' \
|
||||||
'(-i --interactive)-l[List all matching directories]' \
|
'(-i --interactive)-l[List all matching directories]' \
|
||||||
|
@ -53,6 +53,7 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
|||||||
}
|
}
|
||||||
'zoxide;query' {
|
'zoxide;query' {
|
||||||
[CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'Exclude a path from results')
|
[CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'Exclude a path from results')
|
||||||
|
[CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'Show deleted directories')
|
||||||
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
||||||
[CompletionResult]::new('--interactive', 'interactive', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
[CompletionResult]::new('--interactive', 'interactive', [CompletionResultType]::ParameterName, 'Use interactive selection')
|
||||||
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List all matching directories')
|
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List all matching directories')
|
||||||
|
@ -108,7 +108,7 @@ _zoxide() {
|
|||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
zoxide__query)
|
zoxide__query)
|
||||||
opts=" -i -l -s -h --interactive --list --score --exclude --help <keywords>... "
|
opts=" -i -l -s -h --all --interactive --list --score --exclude --help <keywords>... "
|
||||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
|
@ -44,6 +44,7 @@ edit:completion:arg-completer[zoxide] = [@words]{
|
|||||||
}
|
}
|
||||||
&'zoxide;query'= {
|
&'zoxide;query'= {
|
||||||
cand --exclude 'Exclude a path from results'
|
cand --exclude 'Exclude a path from results'
|
||||||
|
cand --all 'Show deleted directories'
|
||||||
cand -i 'Use interactive selection'
|
cand -i 'Use interactive selection'
|
||||||
cand --interactive 'Use interactive selection'
|
cand --interactive 'Use interactive selection'
|
||||||
cand -l 'List all matching directories'
|
cand -l 'List all matching directories'
|
||||||
|
@ -18,6 +18,7 @@ complete -c zoxide -n "__fish_seen_subcommand_from init" -l no-aliases -d 'Preve
|
|||||||
complete -c zoxide -n "__fish_seen_subcommand_from init" -s h -l help -d 'Prints help information'
|
complete -c zoxide -n "__fish_seen_subcommand_from init" -s h -l help -d 'Prints help information'
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -r
|
complete -c zoxide -n "__fish_seen_subcommand_from query" -r
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -l exclude -d 'Exclude a path from results' -r
|
complete -c zoxide -n "__fish_seen_subcommand_from query" -l exclude -d 'Exclude a path from results' -r
|
||||||
|
complete -c zoxide -n "__fish_seen_subcommand_from query" -l all -d 'Show deleted directories'
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s i -l interactive -d 'Use interactive selection'
|
complete -c zoxide -n "__fish_seen_subcommand_from query" -s i -l interactive -d 'Use interactive selection'
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s l -l list -d 'List all matching directories'
|
complete -c zoxide -n "__fish_seen_subcommand_from query" -s l -l list -d 'List all matching directories'
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from query" -s s -l score -d 'Print score with results'
|
complete -c zoxide -n "__fish_seen_subcommand_from query" -s s -l score -d 'Print score with results'
|
||||||
|
@ -15,7 +15,7 @@ If you'd like to prevent a directory from being added to the database, see the
|
|||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B -h, --help
|
.B -h, --help
|
||||||
Prints help information
|
Prints help information.
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
For any issues, feature requests, or questions, please visit:
|
For any issues, feature requests, or questions, please visit:
|
||||||
.sp
|
.sp
|
||||||
|
@ -18,7 +18,7 @@ l l.
|
|||||||
algorithm is too different to import the scores.
|
algorithm is too different to import the scores.
|
||||||
.TP
|
.TP
|
||||||
.B -h, --help
|
.B -h, --help
|
||||||
Prints help information
|
Prints help information.
|
||||||
.TP
|
.TP
|
||||||
.B --merge
|
.B --merge
|
||||||
By default, the import fails if the current database is not already empty. This
|
By default, the import fails if the current database is not already empty. This
|
||||||
|
@ -82,7 +82,7 @@ Changes the prefix of predefined aliases (\fBz\fR, \fBzi\fR).
|
|||||||
e.g. --cmd j would change the aliases to j and ji respectively.
|
e.g. --cmd j would change the aliases to j and ji respectively.
|
||||||
.TP
|
.TP
|
||||||
.B -h, --help
|
.B -h, --help
|
||||||
Prints help information
|
Prints help information.
|
||||||
.TP
|
.TP
|
||||||
.B --hook \fIHOOK\fR
|
.B --hook \fIHOOK\fR
|
||||||
Changes how often zoxide increments a directory's score:
|
Changes how often zoxide increments a directory's score:
|
||||||
|
@ -8,8 +8,11 @@ Queries the database for paths matching the keywords. The exact \fBMATCHING\fR
|
|||||||
algorithm is described in \fBzoxide\fR(1).
|
algorithm is described in \fBzoxide\fR(1).
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
|
.B --all
|
||||||
|
Show deleted directories.
|
||||||
|
.TP
|
||||||
.B -h, --help
|
.B -h, --help
|
||||||
Prints help information
|
Prints help information.
|
||||||
.TP
|
.TP
|
||||||
.B -i, --interactive
|
.B -i, --interactive
|
||||||
Use interactive selection. This option requires fzf.
|
Use interactive selection. This option requires fzf.
|
||||||
|
@ -9,7 +9,7 @@ If you'd like to permanently exclude a directory from the database, see the
|
|||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B -h, --help
|
.B -h, --help
|
||||||
Prints help information
|
Prints help information.
|
||||||
.TP
|
.TP
|
||||||
.B -i, --interactive \fI[KEYWORDS]\fR
|
.B -i, --interactive \fI[KEYWORDS]\fR
|
||||||
Use interactive selection. This option requires fzf.
|
Use interactive selection. This option requires fzf.
|
||||||
|
@ -36,7 +36,7 @@ Remove a directory from the database.
|
|||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B -h, --help
|
.B -h, --help
|
||||||
Prints help information
|
Prints help information.
|
||||||
.TP
|
.TP
|
||||||
.B -V, --version
|
.B -V, --version
|
||||||
Prints version information
|
Prints version information
|
||||||
|
@ -99,6 +99,10 @@ pub enum InitShell {
|
|||||||
pub struct Query {
|
pub struct Query {
|
||||||
pub keywords: Vec<String>,
|
pub keywords: Vec<String>,
|
||||||
|
|
||||||
|
/// Show deleted directories
|
||||||
|
#[clap(long)]
|
||||||
|
pub all: bool,
|
||||||
|
|
||||||
/// Use interactive selection
|
/// Use interactive selection
|
||||||
#[clap(long, short, conflicts_with = "list")]
|
#[clap(long, short, conflicts_with = "list")]
|
||||||
pub interactive: bool,
|
pub interactive: bool,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use super::Run;
|
use crate::app::{Add, Run};
|
||||||
use crate::app::Add;
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::db::DatabaseFile;
|
use crate::db::DatabaseFile;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use super::Run;
|
use crate::app::{Import, ImportFrom, Run};
|
||||||
use crate::app::{Import, ImportFrom};
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::db::{Database, DatabaseFile, Dir, DirList};
|
use crate::db::{Database, DatabaseFile, Dir, DirList};
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use super::Run;
|
use crate::app::{Init, InitShell, Run};
|
||||||
use crate::app::{Init, InitShell};
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::error::WriteErrorHandler;
|
use crate::error::BrokenPipeHandler;
|
||||||
use crate::shell::{self, Opts};
|
use crate::shell::{self, Opts};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use super::Run;
|
use crate::app::{Query, Run};
|
||||||
use crate::app::Query;
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::db::{self, DatabaseFile};
|
use crate::db::{DatabaseFile, Matcher};
|
||||||
use crate::error::WriteErrorHandler;
|
use crate::error::BrokenPipeHandler;
|
||||||
use crate::fzf::Fzf;
|
use crate::fzf::Fzf;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
@ -15,13 +14,16 @@ impl Run for Query {
|
|||||||
let data_dir = config::zo_data_dir()?;
|
let data_dir = config::zo_data_dir()?;
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
let mut db = DatabaseFile::new(data_dir);
|
||||||
let mut db = db.open()?;
|
let mut db = db.open()?;
|
||||||
|
|
||||||
let query = db::Query::new(&self.keywords);
|
|
||||||
let now = util::current_time()?;
|
let now = util::current_time()?;
|
||||||
|
|
||||||
let resolve_symlinks = config::zo_resolve_symlinks();
|
let mut matcher = Matcher::new().with_keywords(&self.keywords);
|
||||||
|
if !self.all {
|
||||||
|
let resolve_symlinks = config::zo_resolve_symlinks();
|
||||||
|
matcher = matcher.with_exists(resolve_symlinks);
|
||||||
|
}
|
||||||
|
|
||||||
let mut matches = db
|
let mut matches = db
|
||||||
.iter_matches(&query, now, resolve_symlinks)
|
.iter(&matcher, now)
|
||||||
.filter(|dir| Some(dir.path.as_ref()) != self.exclude.as_deref());
|
.filter(|dir| Some(dir.path.as_ref()) != self.exclude.as_deref());
|
||||||
|
|
||||||
if self.interactive {
|
if self.interactive {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use super::Run;
|
use crate::app::{Remove, Run};
|
||||||
use crate::app::Remove;
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::db::{DatabaseFile, Query};
|
use crate::db::{DatabaseFile, Matcher};
|
||||||
use crate::error::WriteErrorHandler;
|
use crate::error::BrokenPipeHandler;
|
||||||
use crate::fzf::Fzf;
|
use crate::fzf::Fzf;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
@ -19,12 +18,11 @@ impl Run for Remove {
|
|||||||
let selection;
|
let selection;
|
||||||
match &self.interactive {
|
match &self.interactive {
|
||||||
Some(keywords) => {
|
Some(keywords) => {
|
||||||
let query = Query::new(keywords);
|
let matcher = Matcher::new().with_keywords(keywords);
|
||||||
let now = util::current_time()?;
|
let now = util::current_time()?;
|
||||||
let resolve_symlinks = config::zo_resolve_symlinks();
|
|
||||||
|
|
||||||
let mut fzf = Fzf::new(true)?;
|
let mut fzf = Fzf::new(true)?;
|
||||||
for dir in db.iter_matches(&query, now, resolve_symlinks) {
|
for dir in db.iter(&matcher, now) {
|
||||||
writeln!(fzf.stdin(), "{}", dir.display_score(now)).pipe_exit("fzf")?;
|
writeln!(fzf.stdin(), "{}", dir.display_score(now)).pipe_exit("fzf")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
use super::Query;
|
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use bincode::Options as _;
|
use bincode::Options as _;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::fs;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
@ -95,16 +92,6 @@ pub struct Dir<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Dir<'_> {
|
impl Dir<'_> {
|
||||||
pub fn is_match(&self, query: &Query, resolve_symlinks: bool) -> bool {
|
|
||||||
let resolver = if resolve_symlinks {
|
|
||||||
fs::symlink_metadata
|
|
||||||
} else {
|
|
||||||
fs::metadata
|
|
||||||
};
|
|
||||||
let path = self.path.as_ref();
|
|
||||||
query.matches(path) && resolver(path).map(|m| m.is_dir()).unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn score(&self, now: Epoch) -> Rank {
|
pub fn score(&self, now: Epoch) -> Rank {
|
||||||
const HOUR: Epoch = 60 * 60;
|
const HOUR: Epoch = 60 * 60;
|
||||||
const DAY: Epoch = 24 * HOUR;
|
const DAY: Epoch = 24 * HOUR;
|
||||||
|
@ -2,7 +2,7 @@ mod dir;
|
|||||||
mod query;
|
mod query;
|
||||||
|
|
||||||
pub use dir::{Dir, DirList, Epoch, Rank};
|
pub use dir::{Dir, DirList, Epoch, Rank};
|
||||||
pub use query::Query;
|
pub use query::Matcher;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
@ -72,17 +72,12 @@ impl<'a> Database<'a> {
|
|||||||
self.modified = true;
|
self.modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_matches<'b>(
|
pub fn iter<'i>(&'i mut self, m: &'i Matcher, now: Epoch) -> impl Iterator<Item = &'i Dir> {
|
||||||
&'b mut self,
|
|
||||||
query: &'b Query,
|
|
||||||
now: Epoch,
|
|
||||||
resolve_symlinks: bool,
|
|
||||||
) -> impl DoubleEndedIterator<Item = &'b Dir> {
|
|
||||||
self.dirs
|
self.dirs
|
||||||
.sort_unstable_by_key(|dir| Reverse(OrderedFloat(dir.score(now))));
|
.sort_unstable_by_key(|dir| Reverse(OrderedFloat(dir.score(now))));
|
||||||
self.dirs
|
self.dirs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(move |dir| dir.is_match(&query, resolve_symlinks))
|
.filter(move |dir| m.matches(dir.path.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the directory with `path` from the store.
|
/// Removes the directory with `path` from the store.
|
||||||
|
@ -1,40 +1,72 @@
|
|||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
use std::path;
|
use std::path;
|
||||||
|
|
||||||
pub struct Query(Vec<String>);
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Matcher {
|
||||||
|
keywords: Vec<String>,
|
||||||
|
check_exists: bool,
|
||||||
|
resolve_symlinks: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl Query {
|
impl Matcher {
|
||||||
pub fn new<I, S>(keywords: I) -> Query
|
pub fn new() -> Matcher {
|
||||||
where
|
Matcher::default()
|
||||||
I: IntoIterator<Item = S>,
|
}
|
||||||
S: AsRef<str>,
|
|
||||||
{
|
pub fn with_exists(mut self, resolve_symlinks: bool) -> Matcher {
|
||||||
Query(keywords.into_iter().map(util::to_lowercase).collect())
|
self.check_exists = true;
|
||||||
|
self.resolve_symlinks = resolve_symlinks;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_keywords<S: AsRef<str>>(mut self, keywords: &[S]) -> Matcher {
|
||||||
|
self.keywords = keywords.iter().map(util::to_lowercase).collect();
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches<S: AsRef<str>>(&self, path: S) -> bool {
|
pub fn matches<S: AsRef<str>>(&self, path: S) -> bool {
|
||||||
let keywords = &self.0;
|
self.matches_keywords(&path) && self.matches_exists(path)
|
||||||
let (keywords_last, keywords) = match keywords.split_last() {
|
}
|
||||||
|
|
||||||
|
fn matches_exists<S: AsRef<str>>(&self, path: S) -> bool {
|
||||||
|
if !self.check_exists {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resolver = if self.resolve_symlinks {
|
||||||
|
fs::symlink_metadata
|
||||||
|
} else {
|
||||||
|
fs::metadata
|
||||||
|
};
|
||||||
|
|
||||||
|
resolver(path.as_ref())
|
||||||
|
.map(|m| m.is_dir())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_keywords<S: AsRef<str>>(&self, path: S) -> bool {
|
||||||
|
let (keywords_last, keywords) = match self.keywords.split_last() {
|
||||||
Some(split) => split,
|
Some(split) => split,
|
||||||
None => return true,
|
None => return true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = util::to_lowercase(path);
|
let path = util::to_lowercase(path);
|
||||||
let mut subpath = path.as_str();
|
let mut path = path.as_str();
|
||||||
match subpath.rfind(keywords_last) {
|
match path.rfind(keywords_last) {
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
if subpath[idx + keywords_last.len()..].contains(path::is_separator) {
|
if path[idx + keywords_last.len()..].contains(path::is_separator) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
subpath = &subpath[..idx];
|
path = &path[..idx];
|
||||||
}
|
}
|
||||||
None => return false,
|
None => return false,
|
||||||
}
|
}
|
||||||
|
|
||||||
for keyword in keywords.iter().rev() {
|
for keyword in keywords.iter().rev() {
|
||||||
match subpath.rfind(keyword) {
|
match path.rfind(keyword) {
|
||||||
Some(idx) => subpath = &subpath[..idx],
|
Some(idx) => path = &path[..idx],
|
||||||
None => return false,
|
None => return false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +77,7 @@ impl Query {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Query;
|
use super::Matcher;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query() {
|
fn query() {
|
||||||
@ -72,7 +104,8 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for &(keywords, path, is_match) in CASES {
|
for &(keywords, path, is_match) in CASES {
|
||||||
assert_eq!(is_match, Query::new(keywords).matches(path))
|
let matcher = Matcher::new().with_keywords(keywords);
|
||||||
|
assert_eq!(is_match, matcher.matches(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@ impl Display for SilentExit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WriteErrorHandler {
|
pub trait BrokenPipeHandler {
|
||||||
fn pipe_exit(self, device: &str) -> Result<()>;
|
fn pipe_exit(self, device: &str) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteErrorHandler for io::Result<()> {
|
impl BrokenPipeHandler for io::Result<()> {
|
||||||
fn pipe_exit(self, device: &str) -> Result<()> {
|
fn pipe_exit(self, device: &str) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => bail!(SilentExit { code: 0 }),
|
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => bail!(SilentExit { code: 0 }),
|
||||||
|
Loading…
Reference in New Issue
Block a user