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 ShoptCommand;
impl ShellCommand for ShoptCommand {
fn execute(
&self,
mut context: ShellCommandContext,
) -> LocalBoxFuture<'static, ExecuteResult> {
Box::pin(async move {
let mut set_mode = None; let mut options_to_change = Vec::new();
for arg in context.args.into_iter().peekable() {
let arg_str = arg.to_string_lossy();
match arg_str.as_ref() {
"-s" => {
if set_mode == Some(false) {
let _ = context.stderr.write_line(
"shopt: cannot set and unset options simultaneously",
);
return ExecuteResult::from_exit_code(1);
}
set_mode = Some(true);
}
"-u" => {
if set_mode == Some(true) {
let _ = context.stderr.write_line(
"shopt: cannot set and unset options simultaneously",
);
return ExecuteResult::from_exit_code(1);
}
set_mode = Some(false);
}
_ => {
match parse_option_name(&arg_str) {
Some(opt) => options_to_change.push(opt),
None => {
let _ = context.stderr.write_line(&format!(
"shopt: {}: invalid shell option name",
arg_str
));
return ExecuteResult::from_exit_code(1);
}
}
}
}
}
match set_mode {
Some(enabled) => {
if options_to_change.is_empty() {
let _ = context.stderr.write_line("shopt: option name required");
return ExecuteResult::from_exit_code(1);
}
let changes: Vec<EnvChange> = options_to_change
.into_iter()
.map(|opt| EnvChange::SetOption(opt, enabled))
.collect();
ExecuteResult::Continue(0, changes, Vec::new())
}
None => {
let current_options = context.state.shell_options();
if options_to_change.is_empty() {
let _ = context.stdout.write_line(&format!(
"failglob\t{}",
if current_options.contains(ShellOptions::FAILGLOB) {
"on"
} else {
"off"
}
));
let _ = context.stdout.write_line(&format!(
"globstar\t{}",
if current_options.contains(ShellOptions::GLOBSTAR) {
"on"
} else {
"off"
}
));
let _ = context.stdout.write_line(&format!(
"nullglob\t{}",
if current_options.contains(ShellOptions::NULLGLOB) {
"on"
} else {
"off"
}
));
ExecuteResult::from_exit_code(0)
} else {
let mut any_off = false;
for opt in options_to_change {
let is_on = current_options.contains(opt);
if !is_on {
any_off = true;
}
let name = option_to_name(opt);
let _ = context.stdout.write_line(&format!(
"{}\t{}",
name,
if is_on { "on" } else { "off" }
));
}
ExecuteResult::from_exit_code(if any_off { 1 } else { 0 })
}
}
}
})
}
}
fn parse_option_name(name: &str) -> Option<ShellOptions> {
match name {
"nullglob" => Some(ShellOptions::NULLGLOB),
"failglob" => Some(ShellOptions::FAILGLOB),
"globstar" => Some(ShellOptions::GLOBSTAR),
_ => None,
}
}
fn option_to_name(opt: ShellOptions) -> &'static str {
if opt == ShellOptions::NULLGLOB {
"nullglob"
} else if opt == ShellOptions::FAILGLOB {
"failglob"
} else if opt == ShellOptions::GLOBSTAR {
"globstar"
} else {
"unknown"
}
}