aopt_shell/
shell.rs

1pub(crate) mod bash;
2pub(crate) mod fish;
3pub(crate) mod ps1;
4pub(crate) mod zsh;
5
6use std::ffi::OsStr;
7use std::io::Write;
8
9use crate::acore::opt::Opt;
10use crate::acore::opt::Style;
11use crate::acore::trace;
12use crate::acore::HashMap;
13use crate::value::Values;
14use crate::Error;
15
16pub use bash::Bash;
17pub use fish::Fish;
18pub use ps1::PowerShell;
19pub use zsh::Zsh;
20
21pub trait Complete<O> {
22    type Out;
23    type Ctx<'a>;
24    type Err: Into<Error>;
25
26    fn complete<'a, T, W>(
27        &self,
28        s: &mut T,
29        ctx: &mut Self::Ctx<'a>,
30    ) -> Result<Self::Out, Self::Err>
31    where
32        W: Write,
33        T: Shell<O, W>;
34}
35
36macro_rules! name_iter {
37    ($opt:ident) => {
38        std::iter::once($opt.name()).chain(
39            $opt.alias()
40                .iter()
41                .flat_map(|v| v.iter().map(|v| v.as_str())),
42        )
43    };
44}
45
46pub fn complete_cmd<'a, O, I, F>(arg: &str, opts: I, mut f: F) -> Result<bool, Error>
47where
48    O: Opt + 'a,
49    I: Iterator<Item = &'a O>,
50    F: FnMut(&str, &O) -> Result<(), Error>,
51{
52    let mut found = false;
53
54    for opt in opts.filter(|v| v.mat_style(Style::Cmd)) {
55        for name in name_iter!(opt).filter(|v| v.starts_with(arg)) {
56            trace!("available cmd -> {name}");
57            f(name, opt)?;
58            found = true;
59        }
60    }
61    Ok(found)
62}
63
64pub fn complete_val<'a, O, I, F>(
65    arg: &str,
66    bytes: &[u8],
67    opts: I,
68    values: &mut HashMap<String, Box<dyn Values<O, Err = Error>>>,
69    mut f: F,
70) -> Result<bool, Error>
71where
72    O: Opt + 'a,
73    I: Iterator<Item = &'a O>,
74    F: FnMut(&OsStr, &O) -> Result<(), Error>,
75{
76    complete_eq(arg, bytes, opts, values, |_, val, opt| f(val, opt))
77}
78
79pub fn complete_eq<'a, O, I, F>(
80    arg: &str,
81    bytes: &[u8],
82    opts: I,
83    values: &mut HashMap<String, Box<dyn Values<O, Err = Error>>>,
84    mut f: F,
85) -> Result<bool, Error>
86where
87    O: Opt + 'a,
88    I: Iterator<Item = &'a O>,
89    F: FnMut(&str, &OsStr, &O) -> Result<(), Error>,
90{
91    let mut found = false;
92
93    for opt in opts.filter(|v| v.mat_style(Style::Argument)) {
94        for name in name_iter!(opt).filter(|v| v == &arg) {
95            if let Some(getter) = values.get_mut(name) {
96                for val in getter.get_values(opt)? {
97                    if !val.is_empty() && bytes.is_empty()
98                        || bytes
99                            .iter()
100                            .zip(val.as_encoded_bytes())
101                            .all(|(a, b)| *a == *b)
102                    {
103                        trace!("available opt value -> {}", val.display());
104                        f(arg, val, opt)?;
105                        found = true;
106                    }
107                }
108            }
109        }
110    }
111    Ok(found)
112}
113
114pub fn complete_opt<'a, O, I, F>(arg: &str, opts: I, mut f: F) -> Result<bool, Error>
115where
116    O: Opt + 'a,
117    I: Iterator<Item = &'a O>,
118    F: FnMut(&str, &O) -> Result<(), Error>,
119{
120    let mut found = false;
121
122    for opt in opts.filter(|v| {
123        v.mat_style(Style::Argument)
124            || v.mat_style(Style::Boolean)
125            || v.mat_style(Style::Combined)
126            || v.mat_style(Style::Flag)
127    }) {
128        for name in name_iter!(opt).filter(|v| v.starts_with(arg)) {
129            trace!("available opt -> {name}");
130            f(name, opt)?;
131            found = true;
132        }
133    }
134    Ok(found)
135}
136
137pub trait Shell<O, W> {
138    type Err: Into<Error>;
139
140    fn is_avail(&self, name: &str) -> bool;
141
142    fn set_buff(&mut self, w: W);
143
144    fn write_cmd(&mut self, name: &str, opt: &O) -> Result<(), Self::Err>;
145
146    fn write_opt(&mut self, name: &str, opt: &O) -> Result<(), Self::Err>;
147
148    fn write_pos(&mut self, name: &str, opt: &O) -> Result<(), Self::Err>;
149
150    fn write_val(&mut self, val: &OsStr, opt: &O) -> Result<(), Self::Err>;
151
152    fn write_eq(&mut self, name: &str, val: &OsStr, opt: &O) -> Result<(), Self::Err>;
153
154    fn finish(&mut self) -> Result<(), Self::Err>;
155
156    fn take_buff(&mut self) -> Option<W>;
157}
158
159impl<O, W, E: Into<Error>> Shell<O, W> for Box<dyn Shell<O, W, Err = E>> {
160    type Err = E;
161
162    fn is_avail(&self, name: &str) -> bool {
163        Shell::is_avail(self.as_ref(), name)
164    }
165
166    fn write_cmd(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
167        Shell::write_cmd(self.as_mut(), name, opt)
168    }
169
170    fn write_opt(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
171        Shell::write_opt(self.as_mut(), name, opt)
172    }
173
174    fn write_pos(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
175        Shell::write_pos(self.as_mut(), name, opt)
176    }
177
178    fn write_val(&mut self, val: &OsStr, opt: &O) -> Result<(), Self::Err> {
179        Shell::write_val(self.as_mut(), val, opt)
180    }
181
182    fn write_eq(&mut self, name: &str, val: &OsStr, opt: &O) -> Result<(), Self::Err> {
183        Shell::write_eq(self.as_mut(), name, val, opt)
184    }
185
186    fn set_buff(&mut self, w: W) {
187        Shell::set_buff(self.as_mut(), w);
188    }
189
190    fn finish(&mut self) -> Result<(), Self::Err> {
191        Shell::finish(self.as_mut())
192    }
193
194    fn take_buff(&mut self) -> Option<W> {
195        Shell::take_buff(self.as_mut())
196    }
197}
198
199pub fn wrap<O, W, S: Shell<O, W>>(inner: S) -> Adapter<S> {
200    Adapter { inner }
201}
202
203pub fn wrapref<'a, O, W, S: Shell<O, W>>(inner: &'a mut S) -> AdapterRef<'a, S> {
204    AdapterRef { inner }
205}
206
207pub struct Adapter<T> {
208    pub inner: T,
209}
210
211impl<O, W, T: Shell<O, W>> Shell<O, W> for Adapter<T> {
212    type Err = Error;
213
214    fn is_avail(&self, name: &str) -> bool {
215        self.inner.is_avail(name)
216    }
217
218    fn write_cmd(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
219        self.inner.write_cmd(name, opt).map_err(Into::into)
220    }
221
222    fn write_opt(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
223        self.inner.write_opt(name, opt).map_err(Into::into)
224    }
225
226    fn write_pos(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
227        self.inner.write_pos(name, opt).map_err(Into::into)
228    }
229
230    fn write_val(&mut self, val: &OsStr, opt: &O) -> Result<(), Self::Err> {
231        self.inner.write_val(val, opt).map_err(Into::into)
232    }
233
234    fn write_eq(&mut self, name: &str, val: &OsStr, opt: &O) -> Result<(), Self::Err> {
235        self.inner.write_eq(name, val, opt).map_err(Into::into)
236    }
237
238    fn set_buff(&mut self, w: W) {
239        self.inner.set_buff(w);
240    }
241
242    fn finish(&mut self) -> Result<(), Self::Err> {
243        self.inner.finish().map_err(Into::into)
244    }
245
246    fn take_buff(&mut self) -> Option<W> {
247        self.inner.take_buff()
248    }
249}
250
251pub struct AdapterRef<'a, T> {
252    pub inner: &'a mut T,
253}
254
255impl<'a, O, W, T: Shell<O, W>> Shell<O, W> for AdapterRef<'a, T> {
256    type Err = Error;
257
258    fn is_avail(&self, name: &str) -> bool {
259        self.inner.is_avail(name)
260    }
261
262    fn write_cmd(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
263        self.inner.write_cmd(name, opt).map_err(Into::into)
264    }
265
266    fn write_opt(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
267        self.inner.write_opt(name, opt).map_err(Into::into)
268    }
269
270    fn write_pos(&mut self, name: &str, opt: &O) -> Result<(), Self::Err> {
271        self.inner.write_pos(name, opt).map_err(Into::into)
272    }
273
274    fn write_val(&mut self, val: &OsStr, opt: &O) -> Result<(), Self::Err> {
275        self.inner.write_val(val, opt).map_err(Into::into)
276    }
277
278    fn write_eq(&mut self, name: &str, val: &OsStr, opt: &O) -> Result<(), Self::Err> {
279        self.inner.write_eq(name, val, opt).map_err(Into::into)
280    }
281
282    fn set_buff(&mut self, w: W) {
283        self.inner.set_buff(w);
284    }
285
286    fn finish(&mut self) -> Result<(), Self::Err> {
287        self.inner.finish().map_err(Into::into)
288    }
289
290    fn take_buff(&mut self) -> Option<W> {
291        self.inner.take_buff()
292    }
293}
294
295pub struct Manager<'a, O, W> {
296    gens: Vec<Box<dyn Shell<O, W, Err = Error> + 'a>>,
297}
298
299impl<'a, O, W> Default for Manager<'a, O, W>
300where
301    W: Write + 'a,
302    O: Opt + 'a,
303{
304    fn default() -> Self {
305        Self {
306            gens: vec![
307                Box::new(Bash::new()),
308                Box::new(Fish::new()),
309                Box::new(PowerShell::new()),
310                Box::new(Zsh::new()),
311            ],
312        }
313    }
314}
315
316impl<'a, O: 'a, W: 'a> Manager<'a, O, W> {
317    pub fn new() -> Self {
318        Self { gens: vec![] }
319    }
320
321    pub fn register(&mut self, r#gen: impl Shell<O, W> + 'a) -> &mut Self {
322        self.gens.push(Box::new(wrap(r#gen)));
323        self
324    }
325
326    pub fn find(&self, shell: &str) -> Result<&(dyn Shell<O, W, Err = Error>), Error> {
327        self.gens
328            .iter()
329            .find(|v| v.as_ref().is_avail(shell))
330            .ok_or_else(|| crate::error!("can not find shell type `{shell}`"))
331            .map(|v| v.as_ref())
332    }
333
334    pub fn find_mut(
335        &mut self,
336        shell: &str,
337    ) -> Result<&mut Box<dyn Shell<O, W, Err = Error> + 'a>, Error> {
338        self.gens
339            .iter_mut()
340            .find(|v| v.as_ref().is_avail(shell))
341            .ok_or_else(|| crate::error!("can not find shell type `{shell}`"))
342    }
343
344    pub fn take(&mut self, shell: &str) -> Result<Box<dyn Shell<O, W, Err = Error> + 'a>, Error> {
345        self.gens
346            .iter()
347            .position(|v| v.as_ref().is_avail(shell))
348            .ok_or_else(|| crate::error!("can not find shell type `{shell}`"))
349            .map(|v| self.gens.swap_remove(v))
350    }
351}