Add xargs

This commit is contained in:
Austen Adler 2022-05-08 13:54:29 -04:00
parent 6ead19042d
commit 8f3e1f5dc1
2 changed files with 26 additions and 85 deletions

View File

@ -16,6 +16,7 @@ mod shuf;
mod sort;
mod trim;
mod uniq;
mod xargs;
use clap::{Parser, Subcommand};
use errors::KakMessage;
pub use kak::*;
@ -41,6 +42,7 @@ enum Commands {
#[clap(visible_aliases = &["bc", "eval"])]
MathEval(math_eval::Options),
Trim(trim::Options),
Xargs(xargs::Options),
}
fn main() {
@ -76,6 +78,7 @@ fn run() -> Result<KakMessage, KakMessage> {
Commands::Uniq(o) => uniq::uniq(o),
Commands::MathEval(o) => math_eval::math_eval(o),
Commands::Trim(o) => trim::trim(o),
Commands::Xargs(o) => xargs::xargs(o),
}
}

View File

@ -1,102 +1,40 @@
use crate::{
get_selections_desc, set_selections, set_selections_desc, KakMessage, SelectionWithDesc,
};
use regex::Regex;
use crate::{get_selections_with_desc, set_selections, KakMessage};
use std::{
collections::{hash_map::DefaultHasher, BTreeSet},
hash::{Hash, Hasher},
io::{BufRead, BufReader, Write},
process::{Command, Stdio},
};
#[derive(clap::StructOpt, Debug)]
pub struct Options {
args: Vec<String>,
}
pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
// let mut selections = get_selections()?;
let mut child = Command::new("xargs")
.args(["-0"])
.arg("-0")
.args(&options.args)
.stdin(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");
std::thread::spawn(move || {
for s in crate::get_selections_with_desc()? {
stdin.write_all(s.selection).expect("Failed to write to stdin");
stdin.write_all('\0').expect("Failed to write to stdin");
let handle = std::thread::spawn(move || -> Result<(), KakMessage> {
for s in get_selections_with_desc()? {
write!(stdin, "{}\0", s.content)?;
// stdin
// .write_all(&.as_bytes())
// .expect("Failed to write to stdin");
// stdin.write_all(&[b'\0']).expect("Failed to write to stdin");
}
})
// 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());
Ok(())
});
// Set the new selection types
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()
};
set_selections(BufReader::new(child.stdout.take().expect("Failed to get stdout")).split(b'\0'));
if let Some(regex_match) = (|| {
let captures = options.regex.as_ref()?.captures(key)?;
captures
.get(1)
.or_else(|| captures.get(0))
.map(|m| m.as_str())
})() {
key = regex_match;
}
// stdout.
// Ignore case if requested
let key = if options.ignore_case {
key.to_lowercase()
} else {
// TODO: Do I really need to clone this?
key.to_string()
};
// set_selections(selections.iter())?;
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,
))
Ok(KakMessage(format!("Shuf selections",), None))
}