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}