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