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