simplify the code

This commit is contained in:
sxyazi 2023-10-04 01:59:22 +08:00
parent 88a68fb1ec
commit 35c9c14482
No known key found for this signature in database
2 changed files with 100 additions and 123 deletions

18
Cargo.lock generated
View file

@ -325,15 +325,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "cmdexpand"
version = "0.1.0"
source = "git+https://github.com/ndtoan96/cmdexpand.git?tag=v0.1.0#be905e223c7ed5f52655fb4096c842f2f587f0d7"
dependencies = [
"nom",
"thiserror",
]
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"
@ -383,7 +374,6 @@ dependencies = [
"anyhow", "anyhow",
"async-channel", "async-channel",
"clipboard-win", "clipboard-win",
"cmdexpand",
"config", "config",
"crossterm", "crossterm",
"futures", "futures",
@ -1847,9 +1837,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc1433177506450fe920e46a4f9812d0c211f5dd556da10e731a0a3dfa151f0" checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
dependencies = [ dependencies = [
"indexmap 2.0.2", "indexmap 2.0.2",
"serde", "serde",
@ -1869,9 +1859,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.20.1" version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca676d9ba1a322c1b64eb8045a5ec5c0cfb0c9d08e15e9ff622589ad5221c8fe" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [ dependencies = [
"indexmap 2.0.2", "indexmap 2.0.2",
"serde", "serde",

View file

@ -4,9 +4,9 @@ use anyhow::Result;
use tokio::process::{Child, Command}; use tokio::process::{Child, Command};
pub struct ShellOpt { pub struct ShellOpt {
pub cmd: OsString, pub cmd: OsString,
pub args: Vec<OsString>, pub args: Vec<OsString>,
pub piped: bool, pub piped: bool,
pub orphan: bool, pub orphan: bool,
} }
@ -43,129 +43,116 @@ pub fn shell(opt: ShellOpt) -> Result<Child> {
.spawn()?, .spawn()?,
); );
#[cfg(target_os = "windows")] #[cfg(windows)]
{ {
let args: Vec<String> = opt.args.iter().map(|s| s.to_string_lossy().to_string()).collect(); let args: Vec<String> = opt.args.iter().map(|s| s.to_string_lossy().to_string()).collect();
let expanded_args = cmdparse::parse_cmd_to_args(opt.cmd.to_string_lossy().as_ref(), &args); let args_: Vec<&str> = args.iter().map(|s| s.as_ref()).collect();
let expanded = parser::parse(opt.cmd.to_string_lossy().as_ref(), &args_);
Ok( Ok(
Command::new("cmd") Command::new("cmd")
.arg("/C") .arg("/C")
.args(&expanded_args) .args(&expanded)
.stdin(if opt.piped { Stdio::piped() } else { Stdio::inherit() }) .stdin(opt.stdio())
.stdout(if opt.piped { Stdio::piped() } else { Stdio::inherit() }) .stdout(opt.stdio())
.stderr(if opt.piped { Stdio::piped() } else { Stdio::inherit() }) .stderr(opt.stdio())
.kill_on_drop(true) .kill_on_drop(!opt.orphan)
.spawn()?, .spawn()?,
) )
} }
} }
#[cfg(target_os = "windows")] #[cfg(windows)]
mod cmdparse { mod parser {
pub fn parse_cmd_to_args<T>(cmd: &str, args: &[T]) -> Vec<String> use std::{iter::Peekable, str::Chars};
where
T: AsRef<str>,
{
let mut iter = cmd.chars().peekable();
let mut expanded_args = Vec::new();
while let Some(c) = iter.peek() { pub(super) fn parse(cmd: &str, args: &[&str]) -> Vec<String> {
let mut it = cmd.chars().peekable();
let mut expanded = Vec::new();
while let Some(c) = it.next() {
if c.is_whitespace() { if c.is_whitespace() {
while iter.peek().is_some_and(|_c| _c.is_whitespace()) { continue;
iter.next(); }
} let mut s = String::new();
} else if *c == '\'' {
iter.next();
let mut text = String::new();
loop {
if iter.peek().is_none() {
break;
}
if iter.peek().is_some_and(|_c| *_c == '\'') {
iter.next();
break;
}
get_next_char(&mut iter, &mut text, args);
}
expanded_args.push(text);
} else if *c == '"' {
iter.next();
let mut text = String::new();
loop {
if iter.peek().is_none() {
break;
}
if iter.peek().is_some_and(|_c| *_c == '"') {
iter.next();
break;
}
get_next_char(&mut iter, &mut text, args);
}
expanded_args.push(text);
} else {
if *c == '%' {
let mut tmp_iter = iter.clone();
tmp_iter.next();
if tmp_iter.peek().is_some_and(|_c| *_c == '*') {
iter.next();
iter.next();
for arg in args {
expanded_args.push(arg.as_ref().to_string())
}
continue;
}
}
let mut text = String::new(); if c == '\'' {
loop { s.clear();
if iter.peek().is_none() || iter.peek().is_some_and(|_c| _c.is_whitespace()) { while let Some(c) = it.next() {
if c == '\'' {
break; break;
} }
get_next_char(&mut iter, &mut text, args); next_string(&mut it, args, &mut s, c);
} }
expanded_args.push(text); expanded.push(s);
} else if c == '"' {
s.clear();
while let Some(c) = it.next() {
if c == '"' {
break;
}
next_string(&mut it, args, &mut s, c);
}
expanded.push(s);
} else if c == '%' && it.peek().is_some_and(|&c| c == '*') {
it.next();
for arg in args {
expanded.push(arg.to_string());
}
} else {
s.clear();
next_string(&mut it, args, &mut s, c);
while let Some(c) = it.next() {
if c.is_whitespace() {
break;
}
next_string(&mut it, args, &mut s, c);
}
expanded.push(s);
} }
} }
expanded_args expanded
} }
fn get_next_char<T>( fn next_string(it: &mut Peekable<Chars<'_>>, args: &[&str], s: &mut String, c: char) {
iter: &mut std::iter::Peekable<std::str::Chars<'_>>, if c == '\\' {
text: &mut String, match it.next() {
args: &[T], Some('\\') => s.push('\\'), // \\ ==> \
) where Some('\'') => s.push('\''), // \' ==> '
T: AsRef<str>, Some('"') => s.push('"'), // \" ==> "
{ Some('%') => s.push('%'), // \% ==> %
let ch = iter.next().unwrap(); Some('n') => s.push('\n'), // \n ==> '\n'
if ch == '\\' { Some('t') => s.push('\t'), // \t ==> '\t'
match iter.next() { Some('r') => s.push('\r'), // \r ==> '\r'
Some('n') => text.push('\n'), Some(c) => {
Some('r') => text.push('\r'), s.push('\\');
Some('t') => text.push('\t'), s.push(c);
Some(x) => text.push(x), }
None => (), None => s.push('\\'),
} }
} else if ch == '%' { } else if c == '%' {
if iter.peek().is_some_and(|_c| *_c == '*') { match it.peek() {
iter.next(); Some('*') => {
text.push_str(&args.iter().map(|value| value.as_ref()).collect::<Vec<&str>>().join(" ")); s.push_str(&args.join(" "));
} else { it.next();
let mut num = String::new();
while iter.peek().is_some_and(|_c| _c.is_numeric()) {
num.push(iter.next().unwrap());
} }
if num.is_empty() { Some(n) if n.is_ascii_digit() => {
text.push('%'); let mut pos = n.to_string();
} else { it.next();
let i: usize = num.parse().unwrap(); while let Some(&n) = it.peek() {
if i > 0 { if n.is_ascii_digit() {
text.push_str(args.get(i - 1).map(|value| value.as_ref()).unwrap_or_default()); pos.push(it.next().unwrap());
} else {
break;
}
} }
s.push_str(args.get(pos.parse::<usize>().unwrap() - 1).unwrap_or(&""));
} }
_ => s.push('%'),
} }
} else { } else {
text.push(ch); s.push(c);
} }
} }
@ -175,49 +162,49 @@ mod cmdparse {
#[test] #[test]
fn test_no_quote() { fn test_no_quote() {
let args = parse_cmd_to_args("echo abc xyz %1 %2", &["111", "222"]); let args = parse("echo abc xyz %1 %2", &["111", "222"]);
assert_eq!(args, vec!["echo", "abc", "xyz", "111", "222"]); assert_eq!(args, vec!["echo", "abc", "xyz", "111", "222"]);
let args = parse_cmd_to_args(" echo abc xyz %1 %2 ", &["111", "222"]); let args = parse(" echo abc xyz %1 %2 ", &["111", "222"]);
assert_eq!(args, vec!["echo", "abc", "xyz", "111", "222"]); assert_eq!(args, vec!["echo", "abc", "xyz", "111", "222"]);
} }
#[test] #[test]
fn test_single_quote() { fn test_single_quote() {
let args = parse_cmd_to_args("echo 'abc xyz' '%1' %2", &["111", "222"]); let args = parse("echo 'abc xyz' '%1' %2", &["111", "222"]);
assert_eq!(args, vec!["echo", "abc xyz", "111", "222"]); assert_eq!(args, vec!["echo", "abc xyz", "111", "222"]);
let args = parse_cmd_to_args("echo 'abc \"\"xyz' '%1' %2", &["111", "222"]); let args = parse("echo 'abc \"\"xyz' '%1' %2", &["111", "222"]);
assert_eq!(args, vec!["echo", "abc \"\"xyz", "111", "222"]); assert_eq!(args, vec!["echo", "abc \"\"xyz", "111", "222"]);
} }
#[test] #[test]
fn test_double_quote() { fn test_double_quote() {
let args = parse_cmd_to_args("echo \"abc ' 'xyz\" \"%1\" %2 %3", &["111", "222"]); let args = parse("echo \"abc ' 'xyz\" \"%1\" %2 %3", &["111", "222"]);
assert_eq!(args, vec!["echo", "abc ' 'xyz", "111", "222", ""]); assert_eq!(args, vec!["echo", "abc ' 'xyz", "111", "222", ""]);
} }
#[test] #[test]
fn test_escaped() { fn test_escaped() {
let args = parse_cmd_to_args("echo \"a\tbc ' 'x\nyz\" \"\\%1\" %2 %3", &["111", "22 2"]); let args = parse("echo \"a\tbc ' 'x\nyz\" \"\\%1\" %2 %3", &["111", "22 2"]);
assert_eq!(args, vec!["echo", "a\tbc ' 'x\nyz", "%1", "22 2", ""]); assert_eq!(args, vec!["echo", "a\tbc ' 'x\nyz", "%1", "22 2", ""]);
} }
#[test] #[test]
fn test_percent_star() { fn test_percent_star() {
let args = parse_cmd_to_args("echo %* xyz", &["111", "222"]); let args = parse("echo %* xyz", &["111", "222"]);
assert_eq!(args, vec!["echo", "111", "222", "xyz"]); assert_eq!(args, vec!["echo", "111", "222", "xyz"]);
let args = parse_cmd_to_args("echo '%*' xyz", &["111", "222"]); let args = parse("echo '%*' xyz", &["111", "222"]);
assert_eq!(args, vec!["echo", "111 222", "xyz"]); assert_eq!(args, vec!["echo", "111 222", "xyz"]);
let args = parse_cmd_to_args("echo -C%* xyz", &["111", "222"]); let args = parse("echo -C%* xyz", &["111", "222"]);
assert_eq!(args, vec!["echo", "-C111 222", "xyz"]); assert_eq!(args, vec!["echo", "-C111 222", "xyz"]);
} }
#[test] #[test]
fn test_env_var() { fn test_env_var() {
let args = parse_cmd_to_args(" %EDITOR% %* xyz", &["111", "222"]); let args = parse(" %EDITOR% %* xyz", &["111", "222"]);
assert_eq!(args, vec!["%EDITOR%", "111", "222", "xyz"]); assert_eq!(args, vec!["%EDITOR%", "111", "222", "xyz"]);
} }
} }