use futures::future::LocalBoxFuture;
use crate::shell::types::EnvChange;
use crate::shell::types::ExecuteResult;
use crate::shell::types::ShellOptions;
use super::ShellCommand;
use super::ShellCommandContext;
pub struct SetCommand;
impl ShellCommand for SetCommand {
fn execute(
&self,
mut context: ShellCommandContext,
) -> LocalBoxFuture<'static, ExecuteResult> {
let result = execute_set(&mut context);
Box::pin(futures::future::ready(result))
}
}
fn execute_set(context: &mut ShellCommandContext) -> ExecuteResult {
let args: Vec<String> = context
.args
.iter()
.filter_map(|a| a.to_str().map(|s| s.to_string()))
.collect();
if args.is_empty() {
return ExecuteResult::from_exit_code(0);
}
if args.len() == 1 && args[0] == "-o" {
let opts = context.state.shell_options();
for (name, flag) in NAMED_OPTIONS {
let _ = context.stdout.write_line(&format!(
"{}\t{}",
name,
if opts.contains(*flag) { "on" } else { "off" }
));
}
return ExecuteResult::from_exit_code(0);
}
if args.len() == 1 && args[0] == "+o" {
let opts = context.state.shell_options();
for (name, flag) in NAMED_OPTIONS {
let _ = context.stdout.write_line(&format!(
"set {} {}",
if opts.contains(*flag) { "-o" } else { "+o" },
name,
));
}
return ExecuteResult::from_exit_code(0);
}
let mut changes = Vec::new();
let mut i = 0;
while i < args.len() {
let arg = &args[i];
if (arg == "-o" || arg == "+o") && i + 1 < args.len() {
let enable = arg == "-o";
let option_name = &args[i + 1];
if let Some(option) = parse_option_name(option_name) {
changes.push(EnvChange::SetOption(option, enable));
i += 2;
} else {
let _ = context
.stderr
.write_line(&format!("set: unknown option: {}", option_name));
return ExecuteResult::from_exit_code(1);
}
} else if let Some(option) = parse_short_flag(arg) {
let enable = arg.starts_with('-');
changes.push(EnvChange::SetOption(option, enable));
i += 1;
} else {
let _ = context
.stderr
.write_line(&format!("set: invalid option: {}", arg));
return ExecuteResult::from_exit_code(1);
}
}
ExecuteResult::Continue(0, changes, Vec::new())
}
const NAMED_OPTIONS: &[(&str, ShellOptions)] = &[
("errexit", ShellOptions::ERREXIT),
("pipefail", ShellOptions::PIPEFAIL),
];
fn parse_option_name(name: &str) -> Option<ShellOptions> {
NAMED_OPTIONS
.iter()
.find(|(n, _)| *n == name)
.map(|(_, flag)| *flag)
}
fn parse_short_flag(arg: &str) -> Option<ShellOptions> {
if arg.len() != 2 {
return None;
}
let (sign, letter) = arg.split_at(1);
if sign != "-" && sign != "+" {
return None;
}
match letter {
"e" => Some(ShellOptions::ERREXIT),
_ => None,
}
}