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        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        // todo: refactor
167        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            // reference exists
307            .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}