Add xargs
This commit is contained in:
parent
6ead19042d
commit
8f3e1f5dc1
@ -16,6 +16,7 @@ mod shuf;
|
|||||||
mod sort;
|
mod sort;
|
||||||
mod trim;
|
mod trim;
|
||||||
mod uniq;
|
mod uniq;
|
||||||
|
mod xargs;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use errors::KakMessage;
|
use errors::KakMessage;
|
||||||
pub use kak::*;
|
pub use kak::*;
|
||||||
@ -41,6 +42,7 @@ enum Commands {
|
|||||||
#[clap(visible_aliases = &["bc", "eval"])]
|
#[clap(visible_aliases = &["bc", "eval"])]
|
||||||
MathEval(math_eval::Options),
|
MathEval(math_eval::Options),
|
||||||
Trim(trim::Options),
|
Trim(trim::Options),
|
||||||
|
Xargs(xargs::Options),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -76,6 +78,7 @@ fn run() -> Result<KakMessage, KakMessage> {
|
|||||||
Commands::Uniq(o) => uniq::uniq(o),
|
Commands::Uniq(o) => uniq::uniq(o),
|
||||||
Commands::MathEval(o) => math_eval::math_eval(o),
|
Commands::MathEval(o) => math_eval::math_eval(o),
|
||||||
Commands::Trim(o) => trim::trim(o),
|
Commands::Trim(o) => trim::trim(o),
|
||||||
|
Commands::Xargs(o) => xargs::xargs(o),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
src/xargs.rs
108
src/xargs.rs
@ -1,102 +1,40 @@
|
|||||||
use crate::{
|
use crate::{get_selections_with_desc, set_selections, KakMessage};
|
||||||
get_selections_desc, set_selections, set_selections_desc, KakMessage, SelectionWithDesc,
|
|
||||||
};
|
|
||||||
use regex::Regex;
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::DefaultHasher, BTreeSet},
|
io::{BufRead, BufReader, Write},
|
||||||
hash::{Hash, Hasher},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
args: Vec<String>,
|
||||||
}
|
}
|
||||||
pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
|
||||||
|
// let mut selections = get_selections()?;
|
||||||
|
|
||||||
let mut child = Command::new("xargs")
|
let mut child = Command::new("xargs")
|
||||||
.args(["-0"])
|
.arg("-0")
|
||||||
|
.args(&options.args)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn().expect("Failed to spawn child process");
|
.spawn()
|
||||||
|
.expect("Failed to spawn child process");
|
||||||
|
|
||||||
let mut stdin = child.stdin.take().expect("Failed to open stdin");
|
let mut stdin = child.stdin.take().expect("Failed to open stdin");
|
||||||
std::thread::spawn(move || {
|
let handle = std::thread::spawn(move || -> Result<(), KakMessage> {
|
||||||
for s in crate::get_selections_with_desc()? {
|
for s in get_selections_with_desc()? {
|
||||||
stdin.write_all(s.selection).expect("Failed to write to stdin");
|
write!(stdin, "{}\0", s.content)?;
|
||||||
stdin.write_all('\0').expect("Failed to write to stdin");
|
// stdin
|
||||||
|
// .write_all(&.as_bytes())
|
||||||
|
// .expect("Failed to write to stdin");
|
||||||
|
// stdin.write_all(&[b'\0']).expect("Failed to write to stdin");
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
})
|
set_selections(BufReader::new(child.stdout.take().expect("Failed to get stdout")).split(b'\0'));
|
||||||
// Sort selections so the first element is the xargsue one, not an arbitrary one based on primary selection
|
|
||||||
selections.sort_by_key(|s| s.desc.sort());
|
|
||||||
|
|
||||||
// Set the new selection types
|
// stdout.
|
||||||
let new_selections: Vec<Option<SelectionWithDesc>> = selections
|
|
||||||
.into_iter()
|
|
||||||
// Create a BTreeSet of hashes of selections. This way, string content is not stored, but xargsueness can be determined
|
|
||||||
.scan(BTreeSet::new(), |state, s| {
|
|
||||||
// Strip whitespace if requested
|
|
||||||
let mut key = if options.no_skip_whitespace {
|
|
||||||
s.content.as_str()
|
|
||||||
} else {
|
|
||||||
s.content.trim()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(regex_match) = (|| {
|
// set_selections(selections.iter())?;
|
||||||
let captures = options.regex.as_ref()?.captures(key)?;
|
|
||||||
captures
|
|
||||||
.get(1)
|
|
||||||
.or_else(|| captures.get(0))
|
|
||||||
.map(|m| m.as_str())
|
|
||||||
})() {
|
|
||||||
key = regex_match;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore case if requested
|
Ok(KakMessage(format!("Shuf selections",), None))
|
||||||
let key = if options.ignore_case {
|
|
||||||
key.to_lowercase()
|
|
||||||
} else {
|
|
||||||
// TODO: Do I really need to clone this?
|
|
||||||
key.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
key.hash(&mut hasher);
|
|
||||||
|
|
||||||
// Try inserting to the hash
|
|
||||||
if state.insert(hasher.finish()) {
|
|
||||||
// True if this is a string we haven't seen before
|
|
||||||
Some(Some(s))
|
|
||||||
} else {
|
|
||||||
// Nothing was inserted because we already saw this string
|
|
||||||
// Return Some(None) so the iterator can continue
|
|
||||||
Some(None)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Preallocate so the content and string have the same type, but allocation is not repeated
|
|
||||||
// TODO: Do we really have to do this?
|
|
||||||
let empty_string = String::default();
|
|
||||||
set_selections(new_selections.iter().map(|i| match i {
|
|
||||||
Some(s) => &s.content,
|
|
||||||
None => &empty_string,
|
|
||||||
}))?;
|
|
||||||
|
|
||||||
// Deselect all `None` strings (all rows that have been seen before)
|
|
||||||
let mut new_selections_desc = get_selections_desc()?;
|
|
||||||
new_selections_desc.sort();
|
|
||||||
set_selections_desc(
|
|
||||||
// Refresh seelections_desc because positions have changed
|
|
||||||
new_selections_desc
|
|
||||||
.iter()
|
|
||||||
.zip(new_selections.iter())
|
|
||||||
// If the string was emptied (None), then do not set `sd`
|
|
||||||
.filter_map(|(sd, s)| if s.is_some() { Some(sd) } else { None }),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let old_count = new_selections.len();
|
|
||||||
let new_count = new_selections.iter().flatten().count();
|
|
||||||
|
|
||||||
Ok(KakMessage(
|
|
||||||
format!("{} xargs selections out of {}", new_count, old_count),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user