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 #[allow(clippy::filter_next)]
103 match self
104 .parts
105 .iter()
106 .filter(|s| {
107 !matches!(
108 s,
109 SchemaScopeType::Form(_)
110 | SchemaScopeType::Index(_)
111 | SchemaScopeType::Reference(_)
112 )
113 })
114 .next_back()
115 .unwrap()
116 {
117 SchemaScopeType::Entity(name) => {
118 let real_name = form
119 .map(|f| format!("{name}{f}"))
120 .unwrap_or_else(|| name.to_string());
121 Ok(self.split(&real_name))
122 }
123 SchemaScopeType::Property(last) | SchemaScopeType::Definition(last) => {
124 let entity = self
125 .parts
126 .clone()
127 .into_iter()
128 .filter_map(|s| match s {
129 SchemaScopeType::Entity(t) => Some(t),
130 _ => None,
131 })
132 .next_back();
133
134 if let Some(name) = entity {
135 let parts = [&self.split(&name)[..], &self.split(last)[..]].concat();
136
137 Ok(parts)
138 } else {
139 Err(Error::NotImplemented)
140 }
141 }
142 _ => {
143 let glued: Vec<String> = self
144 .parts
145 .clone()
146 .into_iter()
147 .filter_map(|s| match s {
148 SchemaScopeType::Glue(t) => Some(t),
149 _ => None,
150 })
151 .collect();
152
153 let parts: Vec<String> = glued.iter().flat_map(|s| self.split(s)).collect();
154 if !glued.is_empty() {
155 Ok(parts)
156 } else {
157 Err(Error::CodegenCannotRetrieveNameError(format!(
158 "parts: {:?}",
159 self.parts
160 )))
161 }
162 }
163 }
164 }
165
166 fn split(&self, phrase: &str) -> Vec<String> {
167 let re = Regex::new(r"[A-Z_]").unwrap();
169 let result = re.replace_all(phrase, " $0");
170
171 let t = result
172 .chars()
173 .filter(|c| {
174 c.is_ascii_alphabetic() || c.is_ascii_alphanumeric() || c.is_ascii_whitespace()
175 })
176 .collect::<String>();
177
178 let result: Vec<String> = t
179 .split(' ')
180 .map(|s| s.to_string())
181 .filter(|s| !s.is_empty())
182 .collect::<Vec<_>>();
183
184 result
185 }
186}
187
188impl Default for SchemaScope {
189 fn default() -> Self {
190 Self {
191 scope: vec![],
192 spaces: vec![],
193 naming_strategy: SchemaNamingStrategy::Default,
194 }
195 }
196}
197
198impl SchemaScope {
199 pub fn new(naming_strategy: SchemaNamingStrategy) -> Self {
200 Self {
201 naming_strategy,
202 ..Self::default()
203 }
204 }
205
206 pub fn index(&mut self, index: usize) {
207 self.scope.push(SchemaScopeType::Index(index));
208 }
209
210 pub fn pop(&mut self) {
211 self.scope.pop();
212 }
213
214 pub fn reduce(&mut self, n: usize) {
215 self.scope.truncate(self.scope.len() - n);
216 }
217
218 pub fn len(&self) -> usize {
219 self.scope.len()
220 }
221
222 pub fn is_empty(&self) -> bool {
223 self.scope.is_empty()
224 }
225
226 pub fn property(&mut self, property: &str) -> &mut Self {
227 self.scope
228 .push(SchemaScopeType::Property(property.to_string()));
229 self
230 }
231
232 pub fn entity(&mut self, title: &str) {
233 self.scope.push(SchemaScopeType::Entity(title.to_string()));
234 }
235
236 pub fn form(&mut self, form: &str) {
237 self.scope.push(SchemaScopeType::Form(form.to_string()));
238 }
239
240 pub fn definition(&mut self, form: &str) -> &mut Self {
241 self.scope
242 .push(SchemaScopeType::Definition(form.to_string()));
243 self
244 }
245
246 pub fn reference(&mut self, reference: &str) {
247 self.scope
248 .push(SchemaScopeType::Reference(reference.to_string()));
249 }
250
251 pub fn any(&mut self, property: &str) -> &mut Self {
252 self.scope.push(SchemaScopeType::Any(property.to_string()));
253 self
254 }
255
256 pub fn push_str(&mut self, name: &str, what: &str) -> &mut Self {
257 match name {
258 "property" => self.property(what),
259 "definition" => self.definition(what),
260 _ => self.any(what),
261 }
262 }
263
264 pub fn glue(&mut self, property: &str) -> &mut Self {
265 self.scope.push(SchemaScopeType::Glue(property.to_string()));
266 self
267 }
268
269 pub fn add_spaces(&mut self, spaces: &mut Vec<Space>) -> &mut Self {
270 self.spaces.append(spaces);
271 self
272 }
273
274 pub fn add_space(&mut self, space: Space) -> &mut Self {
275 self.spaces.push(space);
276 self
277 }
278
279 pub fn clear_spaces(&mut self) -> &mut Self {
280 self.spaces.clear();
281 self
282 }
283
284 pub fn pop_space(&mut self) -> &mut Self {
285 self.spaces.pop();
286 self
287 }
288
289 pub fn get_spaces(&mut self) -> Vec<Space> {
290 self.spaces.clone()
291 }
292
293 pub fn namer(&mut self) -> BasicNamer {
294 BasicNamer {
295 parts: self.scope.clone(),
296 }
297 }
298
299 pub fn path(&mut self) -> String {
300 self.scope
301 .iter()
302 .rev()
303 .find_map(|s| match s {
304 SchemaScopeType::Reference(r) => Some(r),
305 _ => None,
306 })
307 .map(|reference| {
309 let mut post = self
310 .scope
311 .rsplit(|sep| *sep == SchemaScopeType::Reference(reference.clone()))
312 .next()
313 .unwrap()
314 .to_vec()
315 .iter()
316 .filter_map(|s| scope_to_string(s.clone()))
317 .collect::<Vec<String>>();
318
319 let mut parts = vec![reference.to_string()];
320 parts.append(&mut post);
321
322 parts.join("/")
323 })
324 .unwrap_or_else(|| format!("{self}"))
325 }
326
327 pub fn is_ambiguous(&mut self) -> bool {
328 if self.scope.len() < 2 {
329 return false;
330 }
331
332 if let Some(SchemaScopeType::Form(form)) = self.scope.get(self.scope.len() - 2) {
333 form == "oneOf" || form == "anyOf"
334 } else {
335 false
336 }
337 }
338
339 pub fn recurse(&self) -> bool {
340 if let Some(SchemaScopeType::Reference(reference)) = self.scope.last() {
341 self.scope
342 .iter()
343 .filter(|r| match r {
344 SchemaScopeType::Reference(r) => r == reference,
345 _ => false,
346 })
347 .count()
348 == 2
349 } else {
350 false
351 }
352 }
353}
354
355impl fmt::Display for SchemaScope {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
357 write!(
358 f,
359 "/{}",
360 self.scope
361 .clone()
362 .into_iter()
363 .filter_map(scope_to_string)
364 .collect::<Vec<String>>()
365 .join("/")
366 )
367 }
368}
369
370fn scope_to_string(s: SchemaScopeType) -> Option<String> {
371 match s {
372 SchemaScopeType::Entity(_) => None,
373 SchemaScopeType::Glue(_) => None,
374 SchemaScopeType::Property(v)
375 | SchemaScopeType::Any(v)
376 | SchemaScopeType::Form(v)
377 | SchemaScopeType::Definition(v) => Some(v),
378 SchemaScopeType::Reference(t) => Some(format!("\x1b[0;32m{t}\x1b[0m")),
379 SchemaScopeType::Index(i) => Some(format!("{i}")),
380 }
381 .map(|s| s.replace('/', "~1"))
382}