aopt_shell/
script.rs

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