forked from denoland/deno_task_shell
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshopt.rs
More file actions
148 lines (136 loc) · 4.39 KB
/
shopt.rs
File metadata and controls
148 lines (136 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2018-2025 the Deno authors. MIT license.
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; // None = query, Some(true) = -s, Some(false) = -u
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);
}
_ => {
// treat as option name
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) => {
// set or unset mode
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 => {
// query mode - print option status
let current_options = context.state.shell_options();
if options_to_change.is_empty() {
// print all options (alphabetical order)
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 {
// print specified options and return non-zero if any are off
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"
}
}