Skip to main content

schematools/
scope.rs

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    // real parts of schema pointer
32    Index(usize),
33    Property(String),
34    Entity(String),
35    Form(String),
36    Definition(String),
37    Reference(String),
38    Any(String),
39
40    // name builder
41    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 = [&current[..], &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        // todo: refactor
168        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            // reference exists
308            .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}