1use std::fmt;
2
3use regex::Regex;
4use serde::Serialize;
5
6use crate::error::Error;
7
8#[derive(Clone, Debug)]
9pub enum SchemaNamingStrategy {
10 Default,
11}
12
13#[derive(Clone, Debug)]
14#[allow(dead_code)]
15pub struct SchemaScope {
16 scope: Vec<SchemaScopeType>,
17 naming_strategy: SchemaNamingStrategy,
18 spaces: Vec<Space>,
19}
20
21#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
22pub enum Space {
23 Tag(String),
24 Operation(String),
25 Id(String),
26 Parameter,
27}
28
29#[derive(Clone, Debug, Eq, PartialEq)]
30enum SchemaScopeType {
31 Index(usize),
33 Property(String),
34 Entity(String),
35 Form(String),
36 Definition(String),
37 Reference(String),
38 Any(String),
39
40 Glue(String),
42}
43
44#[derive(Debug, Clone)]
45pub struct BasicNamer {
46 parts: Vec<SchemaScopeType>,
47}
48
49impl BasicNamer {
50 pub fn simple(&self) -> Result<String, Error> {
51 Ok(self.build(self.parts()?))
52 }
53
54 pub fn build(&self, parts: Vec<String>) -> String {
55 let result = parts
56 .iter()
57 .map(|s| s[..1].to_ascii_uppercase() + &s[1..])
58 .collect::<Vec<_>>()
59 .join("");
60
61 result
62 }
63
64 pub fn convert(&self, original: &str) -> String {
65 self.build(self.split(original))
66 }
67
68 pub fn decorate(&self, parts: Vec<String>) -> String {
69 let current = self.parts().unwrap();
70 let parts = [¤t[..], &parts[..]].concat();
71
72 self.build(parts)
73 }
74
75 fn parts(&self) -> Result<Vec<String>, Error> {
76 if self.parts.is_empty() {
77 return Err(Error::NotImplemented);
78 }
79
80 let form = if self.parts.len() < 2 {
81 None
82 } else if let Some(SchemaScopeType::Form(form)) = self.parts.get(self.parts.len() - 2) {
83 if form == "oneOf" || form == "anyOf" {
84 let last = self.parts.last().unwrap();
85 match last {
86 SchemaScopeType::Index(i) => Some(format!("Option{}", i + 1)),
87 _ => None,
88 }
89 } else if form == "allOf" {
90 let last = self.parts.last().unwrap();
91 match last {
92 SchemaScopeType::Index(i) => Some(format!("Partial{}", i + 1)),
93 _ => None,
94 }
95 } else {
96 None
97 }
98 } else {
99 None
100 };
101
102 match self
103 .parts
104 .iter()
105 .filter(|s| {
106 !matches!(
107 s,
108 SchemaScopeType::Form(_)
109 | SchemaScopeType::Index(_)
110 | SchemaScopeType::Reference(_)
111 )
112 })
113 .next_back()
114 .unwrap()
115 {
116 SchemaScopeType::Entity(name) => {
117 let real_name = form
118 .map(|f| format!("{name}{f}"))
119 .unwrap_or_else(|| name.to_string());
120 Ok(self.split(&real_name))
121 }
122 SchemaScopeType::Property(last) | SchemaScopeType::Definition(last) => {
123 let entity = self
124 .parts
125 .clone()
126 .into_iter()
127 .filter_map(|s| match s {
128 SchemaScopeType::Entity(t) => Some(t),
129 _ => None,
130 })
131 .next_back();
132
133 if let Some(name) = entity {
134 let parts = [&self.split(&name)[..], &self.split(last)[..]].concat();
135
136 Ok(parts)
137 } else {
138 Err(Error::NotImplemented)
139 }
140 }
141 _ => {
142 let glued: Vec<String> = self
143 .parts
144 .clone()
145 .into_iter()
146 .filter_map(|s| match s {
147 SchemaScopeType::Glue(t) => Some(t),
148 _ => None,
149 })
150 .collect();
151
152 let parts: Vec<String> = glued.iter().flat_map(|s| self.split(s)).collect();
153 if !glued.is_empty() {
154 Ok(parts)
155 } else {
156 Err(Error::CodegenCannotRetrieveNameError(format!(
157 "parts: {:?}",
158 self.parts
159 )))
160 }
161 }
162 }
163 }
164
165 fn split(&self, phrase: &str) -> Vec<String> {
166 let re = Regex::new(r"[A-Z_]").unwrap();
168 let result = re.replace_all(phrase, " $0");
169
170 let t = result
171 .chars()
172 .filter(|c| {
173 c.is_ascii_alphabetic() || c.is_ascii_alphanumeric() || c.is_ascii_whitespace()
174 })
175 .collect::<String>();
176
177 let result: Vec<String> = t
178 .split(' ')
179 .map(|s| s.to_string())
180 .filter(|s| !s.is_empty())
181 .collect::<Vec<_>>();
182
183 result
184 }
185}
186
187impl Default for SchemaScope {
188 fn default() -> Self {
189 Self {
190 scope: vec![],
191 spaces: vec![],
192 naming_strategy: SchemaNamingStrategy::Default,
193 }
194 }
195}
196
197impl SchemaScope {
198 pub fn new(naming_strategy: SchemaNamingStrategy) -> Self {
199 Self {
200 naming_strategy,
201 ..Self::default()
202 }
203 }
204
205 pub fn index(&mut self, index: usize) {
206 self.scope.push(SchemaScopeType::Index(index));
207 }
208
209 pub fn pop(&mut self) {
210 self.scope.pop();
211 }
212
213 pub fn reduce(&mut self, n: usize) {
214 self.scope.truncate(self.scope.len() - n);
215 }
216
217 pub fn len(&self) -> usize {
218 self.scope.len()
219 }
220
221 pub fn is_empty(&self) -> bool {
222 self.scope.is_empty()
223 }
224
225 pub fn property(&mut self, property: &str) -> &mut Self {
226 self.scope
227 .push(SchemaScopeType::Property(property.to_string()));
228 self
229 }
230
231 pub fn entity(&mut self, title: &str) {
232 self.scope.push(SchemaScopeType::Entity(title.to_string()));
233 }
234
235 pub fn form(&mut self, form: &str) {
236 self.scope.push(SchemaScopeType::Form(form.to_string()));
237 }
238
239 pub fn definition(&mut self, form: &str) -> &mut Self {
240 self.scope
241 .push(SchemaScopeType::Definition(form.to_string()));
242 self
243 }
244
245 pub fn reference(&mut self, reference: &str) {
246 self.scope
247 .push(SchemaScopeType::Reference(reference.to_string()));
248 }
249
250 pub fn any(&mut self, property: &str) -> &mut Self {
251 self.scope.push(SchemaScopeType::Any(property.to_string()));
252 self
253 }
254
255 pub fn push_str(&mut self, name: &str, what: &str) -> &mut Self {
256 match name {
257 "property" => self.property(what),
258 "definition" => self.definition(what),
259 _ => self.any(what),
260 }
261 }
262
263 pub fn glue(&mut self, property: &str) -> &mut Self {
264 self.scope.push(SchemaScopeType::Glue(property.to_string()));
265 self
266 }
267
268 pub fn add_spaces(&mut self, spaces: &mut Vec<Space>) -> &mut Self {
269 self.spaces.append(spaces);
270 self
271 }
272
273 pub fn add_space(&mut self, space: Space) -> &mut Self {
274 self.spaces.push(space);
275 self
276 }
277
278 pub fn clear_spaces(&mut self) -> &mut Self {
279 self.spaces.clear();
280 self
281 }
282
283 pub fn pop_space(&mut self) -> &mut Self {
284 self.spaces.pop();
285 self
286 }
287
288 pub fn get_spaces(&mut self) -> Vec<Space> {
289 self.spaces.clone()
290 }
291
292 pub fn namer(&mut self) -> BasicNamer {
293 BasicNamer {
294 parts: self.scope.clone(),
295 }
296 }
297
298 pub fn path(&mut self) -> String {
299 self.scope
300 .iter()
301 .rev()
302 .find_map(|s| match s {
303 SchemaScopeType::Reference(r) => Some(r),
304 _ => None,
305 })
306 .map(|reference| {
308 let mut post = self
309 .scope
310 .rsplit(|sep| *sep == SchemaScopeType::Reference(reference.clone()))
311 .next()
312 .unwrap()
313 .to_vec()
314 .iter()
315 .filter_map(|s| scope_to_string(s.clone()))
316 .collect::<Vec<String>>();
317
318 let mut parts = vec![reference.to_string()];
319 parts.append(&mut post);
320
321 parts.join("/")
322 })
323 .unwrap_or_else(|| format!("{self}"))
324 }
325
326 pub fn is_ambiguous(&mut self) -> bool {
327 if self.scope.len() < 2 {
328 return false;
329 }
330
331 if let Some(SchemaScopeType::Form(form)) = self.scope.get(self.scope.len() - 2) {
332 form == "oneOf" || form == "anyOf"
333 } else {
334 false
335 }
336 }
337
338 pub fn recurse(&self) -> bool {
339 if let Some(SchemaScopeType::Reference(reference)) = self.scope.last() {
340 self.scope
341 .iter()
342 .filter(|r| match r {
343 SchemaScopeType::Reference(r) => r == reference,
344 _ => false,
345 })
346 .count()
347 == 2
348 } else {
349 false
350 }
351 }
352}
353
354impl fmt::Display for SchemaScope {
355 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
356 write!(
357 f,
358 "/{}",
359 self.scope
360 .clone()
361 .into_iter()
362 .filter_map(scope_to_string)
363 .collect::<Vec<String>>()
364 .join("/")
365 )
366 }
367}
368
369fn scope_to_string(s: SchemaScopeType) -> Option<String> {
370 match s {
371 SchemaScopeType::Entity(_) => None,
372 SchemaScopeType::Glue(_) => None,
373 SchemaScopeType::Property(v)
374 | SchemaScopeType::Any(v)
375 | SchemaScopeType::Form(v)
376 | SchemaScopeType::Definition(v) => Some(v),
377 SchemaScopeType::Reference(t) => Some(format!("\x1b[0;32m{t}\x1b[0m")),
378 SchemaScopeType::Index(i) => Some(format!("{i}")),
379 }
380 .map(|s| s.replace('/', "~1"))
381}