mirror of
https://github.com/danbulant/rush
synced 2026-05-19 04:18:35 +00:00
improved pipes
This commit is contained in:
parent
a796812556
commit
1e9c49de0f
5 changed files with 214 additions and 203 deletions
77
Cargo.lock
generated
77
Cargo.lock
generated
|
|
@ -176,6 +176,16 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_pipe"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
|
|
@ -228,6 +238,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"filedescriptor",
|
"filedescriptor",
|
||||||
|
"os_pipe",
|
||||||
"termion",
|
"termion",
|
||||||
"utf8-chars",
|
"utf8-chars",
|
||||||
]
|
]
|
||||||
|
|
@ -347,3 +358,69 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name = "rush"
|
name = "rush"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Daniel Bulant <danbulant@gmail.com>"]
|
authors = ["Daniel Bulant <danbulant@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
description = "A simple rust shell"
|
description = "A simple rust shell"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
@ -12,6 +12,7 @@ utf8-chars = "1.0.0"
|
||||||
termion = "1.5.6"
|
termion = "1.5.6"
|
||||||
filedescriptor = "0.8.1"
|
filedescriptor = "0.8.1"
|
||||||
clap = "3.1.0"
|
clap = "3.1.0"
|
||||||
|
os_pipe = "1.1.4"
|
||||||
|
|
||||||
[dependencies.anyhow]
|
[dependencies.anyhow]
|
||||||
version = "1.0.54"
|
version = "1.0.54"
|
||||||
|
|
|
||||||
|
|
@ -670,6 +670,6 @@ pub fn build_tree(tokens: Vec<Token>) -> Result<Vec<Expression>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// dbg!(&expressions);
|
dbg!(&expressions);
|
||||||
Ok(expressions)
|
Ok(expressions)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,89 +14,6 @@ trait GetValue {
|
||||||
fn get(&mut self, ctx: &mut vars::Context) -> Result<Variable>;
|
fn get(&mut self, ctx: &mut vars::Context) -> Result<Variable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExecResult {
|
|
||||||
cmd: Option<Command>,
|
|
||||||
child: Option<Child>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExecResult {
|
|
||||||
fn new(cmd: Option<Command>, child: Option<Child>) -> Self {
|
|
||||||
Self { cmd, child }
|
|
||||||
}
|
|
||||||
/// Spawns the result, running the command (if any). Non-command results won't be spawned (like let statements)
|
|
||||||
fn spawn(&mut self) -> &mut Self {
|
|
||||||
if !self.started() {
|
|
||||||
match &mut self.cmd {
|
|
||||||
None => {},
|
|
||||||
Some(cmd) => {
|
|
||||||
self.child = Some(cmd.spawn().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
/// Checks if the result was spawned before by checking the child property. Non-command results won't ever be spawned (like let statements)
|
|
||||||
fn started(&self) -> bool {
|
|
||||||
matches!(self.child, Some(_))
|
|
||||||
}
|
|
||||||
/// A simple wrapper for redirecting current result (self) into STDIO (files or streams).
|
|
||||||
///
|
|
||||||
/// Does spawn the current result
|
|
||||||
fn redirect_into<T: std::io::Write>(mut self, into: &mut T) -> &mut T {
|
|
||||||
match &mut self.cmd {
|
|
||||||
None => {},
|
|
||||||
Some(cmd) => {
|
|
||||||
cmd.stdout(Stdio::piped());
|
|
||||||
self.spawn();
|
|
||||||
let child = self.child.unwrap();
|
|
||||||
let mut stdout = child.stdout.unwrap();
|
|
||||||
io::copy(&mut stdout, into).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
into
|
|
||||||
}
|
|
||||||
/// A shorthand for redirecting current result into the next one
|
|
||||||
///
|
|
||||||
/// Uses `redirect_from_result` of the next result. Spawns this result, but not the next one.
|
|
||||||
fn redirect_into_result(&mut self, into: &mut ExecResult) -> &mut Self {
|
|
||||||
into.redirect_from_result(self).unwrap();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
/// Redirects the `from` into the current pending result
|
|
||||||
///
|
|
||||||
/// Doesn't spawn the current result
|
|
||||||
fn redirect_from<T: Into<Stdio>>(&mut self, from: T) -> &mut Self {
|
|
||||||
match &mut self.cmd {
|
|
||||||
None => {},
|
|
||||||
Some(cmd) => {
|
|
||||||
cmd.stdin(from);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
/// A shortcut for redirecting a previous result into the current one
|
|
||||||
///
|
|
||||||
/// Spawns the previous result to obtain the output, but not the current one (self)
|
|
||||||
fn redirect_from_result(&mut self, into: &mut ExecResult) -> io::Result<&mut Self> {
|
|
||||||
if matches!(self.cmd, None) {
|
|
||||||
return Ok(self);
|
|
||||||
}
|
|
||||||
match &mut self.cmd {
|
|
||||||
None => {},
|
|
||||||
Some(source) => {
|
|
||||||
source.stdout(Stdio::piped());
|
|
||||||
match &mut into.cmd {
|
|
||||||
None => {},
|
|
||||||
Some(target) => {
|
|
||||||
target.stdin(source.spawn()?.stdout.unwrap());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetValue for CommandValue {
|
impl GetValue for CommandValue {
|
||||||
fn get(self: &mut CommandValue, ctx: &mut vars::Context) -> Result<Variable> {
|
fn get(self: &mut CommandValue, ctx: &mut vars::Context) -> Result<Variable> {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -179,6 +96,31 @@ impl ExecExpression for Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExecExpression for Command {
|
||||||
|
fn exec(&mut self, ctx: &mut Context) -> Result<Option<Command>> {
|
||||||
|
let overrides = ctx.get_overrides()?;
|
||||||
|
if let Some(stdout) = overrides.stdout { self.stdout(stdout); }
|
||||||
|
if let Some(stderr) = overrides.stderr { self.stderr(stderr); }
|
||||||
|
if let Some(stdin) = overrides.stdin { self.stdin(stdin); }
|
||||||
|
let name = self.get_program().to_str().unwrap_or("unknown").to_string();
|
||||||
|
let out = self.spawn()
|
||||||
|
.with_context(|| "Failed to spawn process ".to_string() + &name)?
|
||||||
|
.wait()
|
||||||
|
.with_context(|| "Failed to wait for process")?;
|
||||||
|
ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(-1)));
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecExpression for Option<Command> {
|
||||||
|
fn exec(&mut self, ctx: &mut Context) -> Result<Option<Command>> {
|
||||||
|
match self {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(cmd) => cmd.exec(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ExecExpression for BreakExpression {
|
impl ExecExpression for BreakExpression {
|
||||||
fn exec(self: &mut BreakExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
|
fn exec(self: &mut BreakExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
|
||||||
if ctx.break_num > 0 { ctx.break_num -= 1; return Ok(None) }
|
if ctx.break_num > 0 { ctx.break_num -= 1; return Ok(None) }
|
||||||
|
|
@ -199,26 +141,15 @@ impl ExecExpression for WhileExpression {
|
||||||
ctx.add_scope();
|
ctx.add_scope();
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
loop {
|
loop {
|
||||||
match condition.spawn() {
|
let condres = condition.exec(ctx)?;
|
||||||
Err(_) => {
|
let code = ctx.get_last_exit_code().unwrap_or(1);
|
||||||
res = Some(condition);
|
|
||||||
break;
|
if code == 0 {
|
||||||
},
|
|
||||||
Ok(mut child) => {
|
|
||||||
if !child.wait()?.success() {
|
|
||||||
res = Some(condition);
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
match res {
|
|
||||||
None => {},
|
|
||||||
Some(mut cmd) => {
|
|
||||||
wait_child(cmd.spawn()?, ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = self.contents.exec(ctx)?
|
res = self.contents.exec(ctx)?
|
||||||
|
} else {
|
||||||
|
res = condres;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
if ctx.break_num > 0 {
|
if ctx.break_num > 0 {
|
||||||
ctx.break_num -= 1;
|
ctx.break_num -= 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -261,12 +192,7 @@ impl ExecExpression for ForExpression {
|
||||||
} else {
|
} else {
|
||||||
for (i, val) in arr.iter().enumerate() {
|
for (i, val) in arr.iter().enumerate() {
|
||||||
process(i, val.clone(), ctx, &arg_key, &arg_value)?;
|
process(i, val.clone(), ctx, &arg_key, &arg_value)?;
|
||||||
match res {
|
res.exec(ctx)?;
|
||||||
None => {},
|
|
||||||
Some(mut cmd) => {
|
|
||||||
wait_child(cmd.spawn()?, ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = self.contents.exec(ctx)?;
|
res = self.contents.exec(ctx)?;
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
if ctx.break_num > 0 {
|
if ctx.break_num > 0 {
|
||||||
|
|
@ -282,12 +208,7 @@ impl ExecExpression for ForExpression {
|
||||||
} else {
|
} else {
|
||||||
for (i, char) in str.chars().enumerate() {
|
for (i, char) in str.chars().enumerate() {
|
||||||
process(i, Variable::String(char.to_string()), ctx, &arg_key, &arg_value)?;
|
process(i, Variable::String(char.to_string()), ctx, &arg_key, &arg_value)?;
|
||||||
match res {
|
res.exec(ctx)?;
|
||||||
None => {},
|
|
||||||
Some(mut cmd) => {
|
|
||||||
wait_child(cmd.spawn()?, ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = self.contents.exec(ctx)?;
|
res = self.contents.exec(ctx)?;
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
if ctx.break_num > 0 {
|
if ctx.break_num > 0 {
|
||||||
|
|
@ -312,18 +233,13 @@ impl ExecExpression for IfExpression {
|
||||||
Some(cmd) => cmd
|
Some(cmd) => cmd
|
||||||
};
|
};
|
||||||
ctx.add_scope();
|
ctx.add_scope();
|
||||||
let res = match condition.spawn() {
|
let mut res = condition.exec(ctx)?;
|
||||||
Result::Err(_) => {
|
let code = ctx.get_last_exit_code().unwrap_or(1);
|
||||||
self.else_contents.exec(ctx)?
|
if code == 0 {
|
||||||
},
|
res = self.contents.exec(ctx)?;
|
||||||
Result::Ok(mut res) => {
|
|
||||||
if !res.wait()?.success() {
|
|
||||||
self.else_contents.exec(ctx)?
|
|
||||||
} else {
|
} else {
|
||||||
self.contents.exec(ctx)?
|
res = self.else_contents.exec(ctx)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
|
@ -357,15 +273,14 @@ impl ExecExpression for Vec<CommandValue> {
|
||||||
impl ExecExpression for RedirectTargetExpression {
|
impl ExecExpression for RedirectTargetExpression {
|
||||||
fn exec(self: &mut RedirectTargetExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
|
fn exec(self: &mut RedirectTargetExpression, ctx: &mut vars::Context) -> Result<Option<Command>> {
|
||||||
if ctx.break_num > 0 { return Ok(None) }
|
if ctx.break_num > 0 { return Ok(None) }
|
||||||
|
let (reader, writer) = os_pipe::pipe()?;
|
||||||
let mut src = self.source.exec(ctx)?.unwrap();
|
let mut src = self.source.exec(ctx)?.unwrap();
|
||||||
let mut target = self.target.exec(ctx)?.unwrap();
|
let mut target = self.target.exec(ctx)?.unwrap();
|
||||||
src.stdout(Stdio::piped());
|
target.stdin(reader);
|
||||||
match src.spawn() {
|
ctx.add_scope();
|
||||||
Result::Err(e) => { println!("Error executing: {}", e)},
|
ctx.scopes.last_mut().unwrap().stdout_override = Some(writer);
|
||||||
Result::Ok(res) => {
|
src.exec(ctx)?;
|
||||||
target.stdin(res.stdout.unwrap());
|
ctx.pop_scope();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(target))
|
Ok(Some(target))
|
||||||
}
|
}
|
||||||
|
|
@ -382,23 +297,11 @@ impl ExecExpression for FileTargetExpression {
|
||||||
todo!("Redirect without target file");
|
todo!("Redirect without target file");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let command = match src {
|
let command = match src {
|
||||||
Some(mut cmd) => {
|
Some(mut cmd) => {
|
||||||
cmd.stdout(Stdio::piped());
|
let file = File::create(target.to_string())?;
|
||||||
let file = File::create(target.to_string());
|
cmd.stdout(file);
|
||||||
match file {
|
|
||||||
Result::Err(e) => println!("Error: {}", e),
|
|
||||||
Result::Ok(mut file) => {
|
|
||||||
match cmd.spawn() {
|
|
||||||
Result::Err(e) => {
|
|
||||||
println!("Error executing command: {}", e);
|
|
||||||
},
|
|
||||||
Result::Ok(res) => {
|
|
||||||
io::copy(&mut res.stdout.unwrap(), &mut file)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd
|
cmd
|
||||||
},
|
},
|
||||||
None => { bail!("Invalid command provided for file target"); }
|
None => { bail!("Invalid command provided for file target"); }
|
||||||
|
|
@ -422,10 +325,7 @@ impl ExecExpression for FileSourceExpression {
|
||||||
None => { bail!("Invalid command") },
|
None => { bail!("Invalid command") },
|
||||||
Some(cmd) => cmd
|
Some(cmd) => cmd
|
||||||
};
|
};
|
||||||
let source = match File::open(source) {
|
let source = File::open(source).with_context(|| "Couldn't open file to read")?;
|
||||||
Result::Err(e) => bail!("Cannot open file: {}", e),
|
|
||||||
Result::Ok(file) => file
|
|
||||||
};
|
|
||||||
command.stdin(source);
|
command.stdin(source);
|
||||||
|
|
||||||
Ok(Some(command))
|
Ok(Some(command))
|
||||||
|
|
@ -437,6 +337,7 @@ impl ExecExpression for Vec<Expression> {
|
||||||
if ctx.break_num > 0 { return Ok(None) }
|
if ctx.break_num > 0 { return Ok(None) }
|
||||||
let mut last = None;
|
let mut last = None;
|
||||||
for expr in self {
|
for expr in self {
|
||||||
|
last.exec(ctx)?;
|
||||||
last = expr.exec(ctx)?;
|
last = expr.exec(ctx)?;
|
||||||
if ctx.break_num > 0 { return Ok(last) }
|
if ctx.break_num > 0 { return Ok(last) }
|
||||||
}
|
}
|
||||||
|
|
@ -451,21 +352,14 @@ impl ExecExpression for OrExpression {
|
||||||
None => bail!("Invalid OR expression"),
|
None => bail!("Invalid OR expression"),
|
||||||
Some(cmd) => cmd
|
Some(cmd) => cmd
|
||||||
};
|
};
|
||||||
let res = match first.spawn() {
|
first.exec(ctx)?;
|
||||||
Result::Err(_) => {
|
let code = ctx.get_last_exit_code().unwrap_or(1);
|
||||||
self.second.exec(ctx)?
|
if code == 0 {
|
||||||
},
|
Ok(Some(first))
|
||||||
Result::Ok(mut res) => {
|
|
||||||
if res.wait()?.success() {
|
|
||||||
Some(first)
|
|
||||||
} else {
|
} else {
|
||||||
self.second.exec(ctx)?
|
self.second.exec(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecExpression for AndExpression {
|
impl ExecExpression for AndExpression {
|
||||||
|
|
@ -475,43 +369,20 @@ impl ExecExpression for AndExpression {
|
||||||
None => bail!("Invalid AND expression"),
|
None => bail!("Invalid AND expression"),
|
||||||
Some(cmd) => cmd
|
Some(cmd) => cmd
|
||||||
};
|
};
|
||||||
let res = match first.spawn() {
|
first.exec(ctx)?;
|
||||||
Result::Err(_) => {
|
let code = ctx.get_last_exit_code().unwrap_or(1);
|
||||||
Some(first)
|
if code == 0 {
|
||||||
},
|
self.second.exec(ctx)
|
||||||
Result::Ok(mut res) => {
|
|
||||||
if !res.wait()?.success() {
|
|
||||||
Some(first)
|
|
||||||
} else {
|
} else {
|
||||||
self.second.exec(ctx)?
|
Ok(Some(first))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_child(mut child: Child, ctx: &mut Context) -> Result<()> {
|
|
||||||
let out = child.wait()?;
|
|
||||||
ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(1)));
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) -> Result<()> {
|
pub fn exec_tree(tree: Vec<Expression>, ctx: &mut vars::Context) -> Result<()> {
|
||||||
for mut expression in tree {
|
for mut expression in tree {
|
||||||
let cmd = expression.exec(ctx)?;
|
let mut cmd = expression.exec(ctx)?;
|
||||||
match cmd {
|
cmd.exec(ctx)?;
|
||||||
None => {},
|
|
||||||
Some(mut cmd) => match cmd.spawn() {
|
|
||||||
Result::Err(e) => {
|
|
||||||
println!("Error executing: {}", e);
|
|
||||||
},
|
|
||||||
Result::Ok(mut res) => {
|
|
||||||
wait_child(res, ctx)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ctx.break_num > 0 { bail!("Too many break statements") }
|
if ctx.break_num > 0 { bail!("Too many break statements") }
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use os_pipe::{PipeReader, PipeWriter};
|
||||||
use crate::parser::ast::FunctionDefinitionExpression;
|
use crate::parser::ast::FunctionDefinitionExpression;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -171,6 +172,12 @@ pub enum AnyFunction<'a> {
|
||||||
UserDefined(&'a mut FunctionDefinitionExpression)
|
UserDefined(&'a mut FunctionDefinitionExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Overrides {
|
||||||
|
pub stdin: Option<PipeReader>,
|
||||||
|
pub stdout: Option<PipeWriter>,
|
||||||
|
pub stderr: Option<PipeWriter>
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
/// list of variables
|
/// list of variables
|
||||||
|
|
@ -178,7 +185,10 @@ pub struct Scope {
|
||||||
/// list of functions
|
/// list of functions
|
||||||
pub func: HashMap<String, FunctionDefinitionExpression>,
|
pub func: HashMap<String, FunctionDefinitionExpression>,
|
||||||
/// list of file descriptors, to be closed when the scope is left
|
/// list of file descriptors, to be closed when the scope is left
|
||||||
pub fd: Vec<usize>
|
pub fd: Vec<usize>,
|
||||||
|
pub stdin_override: Option<PipeReader>,
|
||||||
|
pub stdout_override: Option<PipeWriter>,
|
||||||
|
pub stderr_override: Option<PipeWriter>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -213,7 +223,10 @@ impl Context {
|
||||||
let scope = Scope {
|
let scope = Scope {
|
||||||
func: HashMap::new(),
|
func: HashMap::new(),
|
||||||
vars: HashMap::new(),
|
vars: HashMap::new(),
|
||||||
fd: Vec::new()
|
fd: Vec::new(),
|
||||||
|
stdin_override: None,
|
||||||
|
stdout_override: None,
|
||||||
|
stderr_override: None
|
||||||
};
|
};
|
||||||
self.scopes.push(scope);
|
self.scopes.push(scope);
|
||||||
}
|
}
|
||||||
|
|
@ -241,6 +254,14 @@ impl Context {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_last_exit_code(&mut self) -> Option<i32> {
|
||||||
|
let var = self.get_var("?");
|
||||||
|
match var {
|
||||||
|
Some(Variable::I32(int)) => Some(*int),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_var(&mut self, key: String, val: Variable) {
|
pub fn set_var(&mut self, key: String, val: Variable) {
|
||||||
let vars = &mut self.scopes.last_mut().unwrap().vars;
|
let vars = &mut self.scopes.last_mut().unwrap().vars;
|
||||||
if key.starts_with("env::") {
|
if key.starts_with("env::") {
|
||||||
|
|
@ -275,4 +296,45 @@ impl Context {
|
||||||
let func = &mut self.scopes.last_mut().unwrap().func;
|
let func = &mut self.scopes.last_mut().unwrap().func;
|
||||||
func.insert(key, val);
|
func.insert(key, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets relevant overrides. Should only be used before running a command, as it will clone all pipes
|
||||||
|
pub fn get_overrides(&self) -> Result<Overrides> {
|
||||||
|
let mut overrides = Overrides {
|
||||||
|
stdin: None,
|
||||||
|
stdout: None,
|
||||||
|
stderr: None
|
||||||
|
};
|
||||||
|
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
match overrides.stdin {
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
match &scope.stdin_override {
|
||||||
|
Some(stdin) => overrides.stdin = Some(stdin.try_clone()?),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match overrides.stderr {
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
match &scope.stderr_override {
|
||||||
|
Some(stderr) => overrides.stderr = Some(stderr.try_clone()?),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match overrides.stdout {
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
match &scope.stdout_override {
|
||||||
|
Some(stdout) => overrides.stdout = Some(stdout.try_clone()?),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(overrides)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue