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}