aopt_shell/
script.rs

1pub(crate) mod bash;
2pub(crate) mod fish;
3pub(crate) mod ps1;
4pub(crate) mod zsh;
5
6use std::io::Write;
7
8use crate::Error;
9pub use bash::Bash;
10pub use fish::Fish;
11pub use ps1::PowerShell;
12pub use zsh::Zsh;
13
14pub trait Generator {
15    type Err: Into<Error>;
16
17    fn is_avail(&self, name: &str) -> bool;
18
19    fn generate(&self, name: &str, bin: &str) -> Result<String, Self::Err>;
20}
21
22impl<E: Into<Error>> Generator for Box<dyn Generator<Err = E>> {
23    type Err = E;
24
25    fn is_avail(&self, name: &str) -> bool {
26        Generator::is_avail(self.as_ref(), name)
27    }
28
29    fn generate(&self, name: &str, bin: &str) -> Result<String, Self::Err> {
30        Generator::generate(self.as_ref(), name, bin)
31    }
32}
33
34pub fn wrap<T: Generator>(r#gen: T) -> Wrapper<T> {
35    Wrapper(r#gen)
36}
37
38pub fn wrapref<'a, T: Generator>(r#gen: &'a T) -> WrapperRef<'a, T> {
39    WrapperRef(r#gen)
40}
41
42pub struct Wrapper<T: Generator>(pub T);
43
44impl<T: Generator> Generator for Wrapper<T> {
45    type Err = Error;
46
47    fn is_avail(&self, name: &str) -> bool {
48        self.0.is_avail(name)
49    }
50
51    fn generate(&self, name: &str, bin: &str) -> Result<String, Self::Err> {
52        self.0.generate(name, bin).map_err(Into::into)
53    }
54}
55
56pub struct WrapperRef<'a, T: Generator>(pub &'a T);
57
58impl<'a, T: Generator> Generator for WrapperRef<'a, T> {
59    type Err = Error;
60
61    fn is_avail(&self, name: &str) -> bool {
62        self.0.is_avail(name)
63    }
64
65    fn generate(&self, name: &str, bin: &str) -> Result<String, Self::Err> {
66        self.0.generate(name, bin).map_err(Into::into)
67    }
68}
69
70pub struct Manager {
71    gens: Vec<Box<dyn Generator<Err = Error>>>,
72}
73
74impl Default for Manager {
75    fn default() -> Self {
76        Self {
77            gens: vec![
78                Box::new(Bash),
79                Box::new(Fish),
80                Box::new(PowerShell),
81                Box::new(Zsh),
82            ],
83        }
84    }
85}
86
87impl Manager {
88    pub fn new() -> Self {
89        Self { gens: vec![] }
90    }
91
92    pub fn register(&mut self, r#gen: impl Generator + 'static) -> &mut Self {
93        self.gens.push(Box::new(wrap(r#gen)));
94        self
95    }
96
97    pub fn find(&self, shell: &str) -> Result<&dyn Generator<Err = Error>, Error> {
98        self.gens
99            .iter()
100            .find(|v| v.is_avail(shell))
101            .ok_or_else(|| crate::error!("can not find generator for shell `{shell}`"))
102            .map(|v| v.as_ref())
103    }
104
105    pub fn find_mut(&mut self, shell: &str) -> Result<&mut Box<dyn Generator<Err = Error>>, Error> {
106        self.gens
107            .iter_mut()
108            .find(|v| v.is_avail(shell))
109            .ok_or_else(|| crate::error!("can not find generator for shell `{shell}`"))
110    }
111
112    pub fn take(&mut self, shell: &str) -> Result<Box<dyn Generator<Err = Error>>, Error> {
113        self.gens
114            .iter()
115            .position(|v| v.is_avail(shell))
116            .ok_or_else(|| crate::error!("can not find generator for shell `{shell}`"))
117            .map(|v| self.gens.swap_remove(v))
118    }
119
120    pub fn generate(&self, shell: &str, name: &str, bin: &str) -> Result<String, Error> {
121        self.find(shell)?.generate(name, bin)
122    }
123
124    pub fn write<W>(&self, shell: &str, name: &str, bin: &str, w: &mut W) -> Result<(), Error>
125    where
126        W: Write,
127    {
128        write!(w, "{}", self.generate(shell, name, bin)?)
129            .map_err(|e| crate::error!("can not write script: {e:?}"))?;
130        Ok(())
131    }
132}