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