1use crate::error::{ValidationError, ValidationResult};
4use crate::provider::{OptionDef, OptionProvider, OptionValueType, ValueValidator};
5use std::collections::HashMap;
6
7pub struct LeptosProvider {
9 options: HashMap<String, Vec<OptionDef>>,
10}
11
12impl Default for LeptosProvider {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl LeptosProvider {
19 pub fn new() -> Self {
20 Self {
21 options: build_leptos_options(),
22 }
23 }
24}
25
26impl OptionProvider for LeptosProvider {
27 fn name(&self) -> &str {
28 "leptos"
29 }
30
31 fn get_options(&self, command: &str) -> Vec<OptionDef> {
32 let leptos_command = if let Some(stripped) = command.strip_prefix("leptos ") {
34 stripped } else {
36 command
37 };
38
39 self.options
40 .get(leptos_command)
41 .cloned()
42 .unwrap_or_default()
43 }
44
45 fn validate(&self, command: &str, option: &str, value: Option<&str>) -> ValidationResult<()> {
46 let options = self.get_options(command);
47
48 let option_def = options
50 .iter()
51 .find(|def| def.name == option)
52 .ok_or_else(|| ValidationError::unknown_option(command, option, vec![]))?;
53
54 match (&option_def.value_type, value) {
56 (OptionValueType::Flag, Some(val)) => Err(ValidationError::UnexpectedValue {
57 option: option.to_string(),
58 value: val.to_string(),
59 }),
60 (OptionValueType::Single(validator), Some(val)) => {
61 validator.validate(val).map_err(|mut e| {
62 if let ValidationError::InvalidValue {
63 option: ref mut opt,
64 ..
65 } = e
66 {
67 *opt = option.to_string();
68 }
69 e
70 })
71 }
72 (OptionValueType::Multiple(validator), Some(val)) => {
73 for v in val.split(',') {
75 validator.validate(v.trim()).map_err(|mut e| {
76 if let ValidationError::InvalidValue {
77 option: ref mut opt,
78 ..
79 } = e
80 {
81 *opt = option.to_string();
82 }
83 e
84 })?;
85 }
86 Ok(())
87 }
88 (OptionValueType::Single(_) | OptionValueType::Multiple(_), None) => {
89 Err(ValidationError::MissingValue {
90 option: option.to_string(),
91 })
92 }
93 (OptionValueType::Flag, None) => Ok(()),
94 }
95 }
96
97 fn get_commands(&self) -> Vec<String> {
98 vec![
99 "leptos build".to_string(),
100 "leptos serve".to_string(),
101 "leptos watch".to_string(),
102 "leptos new".to_string(),
103 "leptos end-to-end".to_string(),
104 ]
105 }
106
107 fn supports_command(&self, command: &str) -> bool {
108 command.starts_with("leptos ") || self.get_commands().contains(&command.to_string())
109 }
110}
111
112fn build_leptos_options() -> HashMap<String, Vec<OptionDef>> {
114 let mut options = HashMap::new();
115
116 options.insert(
118 "build".to_string(),
119 vec![
120 OptionDef::flag("--release", "Build in release mode"),
121 OptionDef::multiple(
122 "--bin-features",
123 "Features to enable for binary compilation",
124 ValueValidator::Any,
125 ),
126 OptionDef::multiple(
127 "--lib-features",
128 "Features to enable for library compilation",
129 ValueValidator::Any,
130 ),
131 OptionDef::flag("--precompress", "Pre-compress static assets"),
132 OptionDef::single(
133 "--bin-target-dir",
134 "Directory for binary artifacts",
135 ValueValidator::DirectoryPath,
136 ),
137 OptionDef::single(
138 "--lib-target-dir",
139 "Directory for library artifacts",
140 ValueValidator::DirectoryPath,
141 ),
142 OptionDef::single(
143 "--site-root",
144 "Root directory for the site",
145 ValueValidator::DirectoryPath,
146 ),
147 OptionDef::single(
148 "--site-pkg-dir",
149 "Directory for site packages",
150 ValueValidator::DirectoryPath,
151 ),
152 OptionDef::single(
153 "--site-addr",
154 "Address to serve the site",
155 ValueValidator::Any,
156 ),
157 OptionDef::single(
158 "--reload-port",
159 "Port for reload server",
160 ValueValidator::Number,
161 ),
162 OptionDef::flag("--hot-reload", "Enable hot reloading"),
163 OptionDef::flag("--no-default-features", "Disable default features"),
164 ],
165 );
166
167 options.insert(
169 "serve".to_string(),
170 vec![
171 OptionDef::flag("--release", "Serve release build"),
172 OptionDef::single("--port", "Port to serve on", ValueValidator::Number),
173 OptionDef::single("--host", "Host to serve on", ValueValidator::Any),
174 OptionDef::flag("--hot-reload", "Enable hot reloading"),
175 OptionDef::single(
176 "--reload-port",
177 "Port for reload server",
178 ValueValidator::Number,
179 ),
180 OptionDef::single(
181 "--site-root",
182 "Root directory for the site",
183 ValueValidator::DirectoryPath,
184 ),
185 OptionDef::single(
186 "--site-pkg-dir",
187 "Directory for site packages",
188 ValueValidator::DirectoryPath,
189 ),
190 OptionDef::multiple(
191 "--bin-features",
192 "Features to enable for binary compilation",
193 ValueValidator::Any,
194 ),
195 OptionDef::multiple(
196 "--lib-features",
197 "Features to enable for library compilation",
198 ValueValidator::Any,
199 ),
200 ],
201 );
202
203 options.insert(
205 "watch".to_string(),
206 vec![
207 OptionDef::flag("--release", "Watch release build"),
208 OptionDef::flag("--hot-reload", "Enable hot reloading"),
209 OptionDef::single(
210 "--reload-port",
211 "Port for reload server",
212 ValueValidator::Number,
213 ),
214 OptionDef::single(
215 "--site-root",
216 "Root directory for the site",
217 ValueValidator::DirectoryPath,
218 ),
219 OptionDef::multiple(
220 "--bin-features",
221 "Features to enable for binary compilation",
222 ValueValidator::Any,
223 ),
224 OptionDef::multiple(
225 "--lib-features",
226 "Features to enable for library compilation",
227 ValueValidator::Any,
228 ),
229 OptionDef::flag("--no-default-features", "Disable default features"),
230 ],
231 );
232
233 options.insert(
235 "new".to_string(),
236 vec![
237 OptionDef::single("--name", "Name of the new project", ValueValidator::Any),
238 OptionDef::single(
239 "--template",
240 "Template to use",
241 ValueValidator::Enum(vec![
242 "start".to_string(),
243 "start-axum".to_string(),
244 "start-actix".to_string(),
245 "csr".to_string(),
246 "ssr".to_string(),
247 ]),
248 ),
249 OptionDef::flag("--git", "Initialize git repository"),
250 OptionDef::flag("--no-git", "Don't initialize git repository"),
251 ],
252 );
253
254 options.insert(
256 "end-to-end".to_string(),
257 vec![
258 OptionDef::flag("--release", "Run end-to-end tests in release mode"),
259 OptionDef::single("--port", "Port for test server", ValueValidator::Number),
260 OptionDef::multiple(
261 "--bin-features",
262 "Features to enable for binary compilation",
263 ValueValidator::Any,
264 ),
265 OptionDef::multiple(
266 "--lib-features",
267 "Features to enable for library compilation",
268 ValueValidator::Any,
269 ),
270 ],
271 );
272
273 options
274}