1use crate::config::model::{Config, SpecEntry};
2use crate::error::{ConfigError, GenerationError, Result};
3use dialoguer::Select;
4
5pub fn list_specs(config: &Config) -> Vec<SpecEntry> {
7 config.specs.clone()
8}
9
10pub fn get_spec_by_name(config: &Config, name: &str) -> Result<SpecEntry> {
12 config
13 .specs
14 .iter()
15 .find(|s| s.name == name)
16 .cloned()
17 .ok_or_else(|| {
18 let available: Vec<String> = config.specs.iter().map(|s| s.name.clone()).collect();
19 GenerationError::SpecNotFound {
20 name: name.to_string(),
21 available,
22 }
23 .into()
24 })
25}
26
27pub fn resolve_spec_selection(
29 config: &Config,
30 cli_spec: Option<String>,
31 all_specs: bool,
32) -> Result<Vec<SpecEntry>> {
33 let specs = list_specs(config);
34
35 if specs.is_empty() {
36 return Err(ConfigError::NoSpecDefined.into());
37 }
38
39 if all_specs {
40 Ok(specs)
42 } else if let Some(spec_name) = cli_spec {
43 let spec = get_spec_by_name(config, &spec_name)?;
45 Ok(vec![spec])
46 } else if specs.len() == 1 {
47 Ok(specs)
49 } else {
50 let spec_names: Vec<String> = specs.iter().map(|s| s.name.clone()).collect();
52 let selection = Select::new()
53 .with_prompt("Which spec do you want to generate?")
54 .items(&spec_names)
55 .interact()
56 .map_err(|e| GenerationError::InvalidOperation {
57 message: format!("Failed to get user selection: {}", e),
58 })?;
59
60 let selected_spec =
61 specs
62 .get(selection)
63 .ok_or_else(|| GenerationError::InvalidOperation {
64 message: "Invalid selection".to_string(),
65 })?;
66
67 Ok(vec![selected_spec.clone()])
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::config::model::{ApisConfig, ModulesConfig, SchemasConfig};
75
76 #[test]
77 fn test_list_specs_multi_mode() {
78 let config = Config {
79 specs: vec![
80 SpecEntry {
81 name: "auth".to_string(),
82 path: "specs/auth.yaml".to_string(),
83 schemas: SchemasConfig::default(),
84 apis: ApisConfig::default(),
85 hooks: None,
86 modules: ModulesConfig::default(),
87 },
88 SpecEntry {
89 name: "orders".to_string(),
90 path: "specs/orders.json".to_string(),
91 schemas: SchemasConfig::default(),
92 apis: ApisConfig::default(),
93 hooks: None,
94 modules: ModulesConfig::default(),
95 },
96 ],
97 ..Default::default()
98 };
99
100 let specs = list_specs(&config);
101 assert_eq!(specs.len(), 2);
102 assert_eq!(specs[0].name, "auth");
103 assert_eq!(specs[1].name, "orders");
104 }
105
106 #[test]
107 fn test_get_spec_by_name_single_mode() {
108 let config = Config {
109 specs: vec![SpecEntry {
110 name: "default".to_string(),
111 path: "openapi.json".to_string(),
112 schemas: SchemasConfig::default(),
113 apis: ApisConfig::default(),
114 hooks: None,
115 modules: ModulesConfig::default(),
116 }],
117 ..Default::default()
118 };
119
120 let spec = get_spec_by_name(&config, "default").unwrap();
121 assert_eq!(spec.name, "default");
122 assert_eq!(spec.path, "openapi.json");
123
124 let result = get_spec_by_name(&config, "auth");
125 assert!(result.is_err());
126 }
127
128 #[test]
129 fn test_get_spec_by_name_multi_mode() {
130 let config = Config {
131 specs: vec![
132 SpecEntry {
133 name: "auth".to_string(),
134 path: "specs/auth.yaml".to_string(),
135 schemas: SchemasConfig::default(),
136 apis: ApisConfig::default(),
137 hooks: None,
138 modules: ModulesConfig::default(),
139 },
140 SpecEntry {
141 name: "orders".to_string(),
142 path: "specs/orders.json".to_string(),
143 schemas: SchemasConfig::default(),
144 apis: ApisConfig::default(),
145 hooks: None,
146 modules: ModulesConfig::default(),
147 },
148 ],
149 ..Default::default()
150 };
151
152 let spec = get_spec_by_name(&config, "auth").unwrap();
153 assert_eq!(spec.name, "auth");
154 assert_eq!(spec.path, "specs/auth.yaml");
155
156 let result = get_spec_by_name(&config, "nonexistent");
157 assert!(result.is_err());
158 }
159
160 #[test]
161 fn test_resolve_spec_selection_all_specs() {
162 let config = Config {
163 specs: vec![
164 SpecEntry {
165 name: "auth".to_string(),
166 path: "specs/auth.yaml".to_string(),
167 schemas: SchemasConfig::default(),
168 apis: ApisConfig::default(),
169 hooks: None,
170 modules: ModulesConfig::default(),
171 },
172 SpecEntry {
173 name: "orders".to_string(),
174 path: "specs/orders.json".to_string(),
175 schemas: SchemasConfig::default(),
176 apis: ApisConfig::default(),
177 hooks: None,
178 modules: ModulesConfig::default(),
179 },
180 ],
181 ..Default::default()
182 };
183
184 let specs = resolve_spec_selection(&config, None, true).unwrap();
185 assert_eq!(specs.len(), 2);
186 }
187
188 #[test]
189 fn test_resolve_spec_selection_specific_spec() {
190 let config = Config {
191 specs: vec![
192 SpecEntry {
193 name: "auth".to_string(),
194 path: "specs/auth.yaml".to_string(),
195 schemas: SchemasConfig::default(),
196 apis: ApisConfig::default(),
197 hooks: None,
198 modules: ModulesConfig::default(),
199 },
200 SpecEntry {
201 name: "orders".to_string(),
202 path: "specs/orders.json".to_string(),
203 schemas: SchemasConfig::default(),
204 apis: ApisConfig::default(),
205 hooks: None,
206 modules: ModulesConfig::default(),
207 },
208 ],
209 ..Default::default()
210 };
211
212 let specs = resolve_spec_selection(&config, Some("auth".to_string()), false).unwrap();
213 assert_eq!(specs.len(), 1);
214 assert_eq!(specs[0].name, "auth");
215 }
216
217 #[test]
218 fn test_resolve_spec_selection_single_mode() {
219 let config = Config {
220 specs: vec![SpecEntry {
221 name: "default".to_string(),
222 path: "openapi.json".to_string(),
223 schemas: SchemasConfig::default(),
224 apis: ApisConfig::default(),
225 hooks: None,
226 modules: ModulesConfig::default(),
227 }],
228 ..Default::default()
229 };
230
231 let specs = resolve_spec_selection(&config, None, false).unwrap();
232 assert_eq!(specs.len(), 1);
233 assert_eq!(specs[0].name, "default");
234 }
235}