arc_isle/
schema.rs

1// mod
2
3use crate::parser::utils::ReadError;
4use std::{
5    collections::HashMap,
6    error::Error,
7    fmt::{Debug, Display, Formatter},
8};
9
10pub struct Schema {
11    pub hosts: Hosts,
12    pub versioning: Versioning,
13    pub types: TypeDeclResults,
14    pub interfaces: InterfaceDeclResults,
15}
16
17impl Display for Schema {
18    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19        let mut result = "Schema {\n".to_string();
20        result.push_str(&format!("  hosts = {:?}\n", self.hosts));
21        result.push_str(&format!("  versioning = {:?}\n", self.versioning));
22        result.push_str(&format!(
23            "  types = {}\n",
24            self.types
25                .iter()
26                .map(|t| {
27                    match t {
28                        Ok(val) => format!("{}\n", val),
29                        Err(err) => format!("{}\n", err),
30                    }
31                })
32                .collect::<String>()
33        ));
34        result.push_str(&format!(
35            "  interfaces = {}\n",
36            self.interfaces
37                .iter()
38                .map(|t| {
39                    match t {
40                        Ok(val) => format!("{}\n", val),
41                        Err(err) => format!("{}\n", err),
42                    }
43                })
44                .collect::<String>()
45        ));
46        f.write_str(&result)
47    }
48}
49
50#[derive(Debug)]
51pub struct Host {
52    pub env: String,
53    pub address: String,
54}
55
56pub type Hosts = Vec<Host>;
57
58#[derive(Debug)]
59pub enum VersioningFormat {
60    Headers,
61}
62
63#[derive(Debug)]
64pub struct Versioning {
65    pub format: VersioningFormat,
66    pub header: Option<String>,
67}
68
69pub type TypeDeclResults = Vec<Result<TypeDecl, TypeDeclError>>;
70
71#[derive(PartialEq, Clone, Debug)]
72pub struct TypeDecl {
73    pub name: String,
74    pub property_decls: Vec<PropertyDecl>,
75}
76
77impl Display for TypeDecl {
78    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
79        let mut result = format!("type `{}` {{ ", self.name);
80        for property_decl in &self.property_decls {
81            match &property_decl.data_type_decl {
82                Ok(data_type_decl) => {
83                    result.push_str(&format!("{}: {}; ", property_decl.name, data_type_decl));
84                }
85                Err(err) => {
86                    result.push_str(&format!("{}: {}; ", property_decl.name, err));
87                }
88            }
89        }
90        result.push_str(" }");
91        f.write_str(&result)
92    }
93}
94
95#[derive(Debug, PartialEq, Clone)]
96pub struct PropertyDecl {
97    pub name: String,
98    pub data_type_decl: Result<DataTypeDecl, TypeDeclError>,
99}
100
101#[derive(Debug, PartialEq, Clone)]
102pub enum TypeDeclError {
103    ImportFailure(ImportError),
104    UnsupportedTypeDeclaration,
105    UnsupportedKeyType,
106    EmptyTypeDeclaration,
107    SubtypeValuesEmptyDeclaration,
108    UnsupportedPrimitive(String),
109}
110
111impl TypeDeclError {
112    fn default_fmt(&self, f: &mut Formatter) -> std::fmt::Result {
113        match self {
114            TypeDeclError::ImportFailure(import_error) => {
115                write!(f, "Import failed: {}", import_error.to_string())
116            }
117            TypeDeclError::UnsupportedTypeDeclaration => {
118                write!(f, "This type declaration format is not supported.")
119            }
120            TypeDeclError::UnsupportedKeyType => write!(f, "Key type must be string."),
121            TypeDeclError::EmptyTypeDeclaration => write!(f, "Type declaration cannot be empty."),
122            TypeDeclError::SubtypeValuesEmptyDeclaration => {
123                write!(f, "Subtype declaration cannot be empty.")
124            }
125            TypeDeclError::UnsupportedPrimitive(value) => {
126                write!(f, "Primitive {} not supported.", value)
127            }
128        }
129    }
130}
131
132impl Error for TypeDeclError {}
133
134impl Display for TypeDeclError {
135    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
136        self.default_fmt(f)
137    }
138}
139
140#[derive(PartialEq, Clone, Debug)]
141pub struct DataTypeDecl {
142    pub data_type: DataType,
143    pub is_required: bool,
144}
145
146impl Display for DataTypeDecl {
147    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
148        let mut result = format!("{}", self.data_type);
149        if !self.is_required {
150            result.push_str("?");
151        }
152        f.write_str(&result)
153    }
154}
155
156#[derive(PartialEq, Clone, Debug)]
157pub enum DataType {
158    Primitive(Primitive),
159    Array(Box<DataType>),
160    Dict(Primitive, Box<DataType>),
161    Object(String),
162    ObjectDecl(TypeDecl),
163}
164
165impl Display for DataType {
166    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
167        match self {
168            DataType::Primitive(primitive) => f.write_str(&format!("{}", primitive)),
169            DataType::Array(data_type) => f.write_str(&format!("array[{}]", data_type)),
170            DataType::Dict(key, value) => f.write_str(&format!("dict{{ {}: {} }}", key, value)),
171            DataType::Object(ident) => f.write_str(&format!("{}", ident)),
172            DataType::ObjectDecl(type_decl) => f.write_str(&format!("{}", type_decl)),
173        }
174    }
175}
176
177#[derive(Debug, PartialEq, Clone)]
178pub enum Primitive {
179    Int,
180    Double,
181    Bool,
182    Str,
183}
184
185impl Display for Primitive {
186    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187        match self {
188            Primitive::Int => f.write_str("int"),
189            Primitive::Double => f.write_str("double"),
190            Primitive::Bool => f.write_str("bool"),
191            Primitive::Str => f.write_str("str"),
192        }
193    }
194}
195
196#[derive(Clone)]
197pub enum ImportError {
198    IOError(ReadError),
199    InvalidInputSource,
200    InvalidImportValue,
201}
202
203impl PartialEq for ImportError {
204    fn eq(&self, other: &Self) -> bool {
205        match (self, other) {
206            (ImportError::IOError(lhs), ImportError::IOError(rhs)) => lhs == rhs,
207            (ImportError::InvalidInputSource, ImportError::InvalidInputSource) => true,
208            (ImportError::InvalidImportValue, ImportError::InvalidImportValue) => true,
209            _ => false,
210        }
211    }
212}
213
214// MARK - Interface
215
216pub type InterfaceDeclResults = Vec<Result<InterfaceDecl, InterfaceDeclError>>;
217
218#[derive(PartialEq, Debug)]
219pub struct InterfaceDecl {
220    pub ident: String,
221    pub params: Vec<String>,
222    pub spec: InterfaceSpec,
223}
224
225impl Display for InterfaceDecl {
226    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
227        let InterfaceSpec::Api(api) = &self.spec;
228        let mut result = format!("{:?} /{}\n", api.method, self.ident);
229        if let Some(payload) = &api.payload {
230            match payload {
231                HttpPayload::Body(body) => {
232                    result.push_str(&format!("Body: {:?}\n", body));
233                }
234                HttpPayload::Query(query) => {
235                    result.push_str(&format!("Query: {:?}\n", query));
236                }
237            }
238        }
239        if let Some(responses) = &api.responses {
240            result.push_str(&format!(
241                "Responses: {}",
242                responses
243                    .iter()
244                    .map(|(k, v)| { format!("{}: {}\n", k, v) })
245                    .collect::<String>()
246            ));
247        }
248        f.write_str(&result)
249    }
250}
251
252#[derive(PartialEq)]
253pub enum InterfaceSpec {
254    Api(ApiSpec),
255}
256
257impl Debug for InterfaceSpec {
258    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
259        match self {
260            InterfaceSpec::Api(api_spec) => f.write_str(&format!("{}", api_spec)),
261        }
262    }
263}
264
265#[derive(PartialEq, Debug)]
266pub struct ApiSpec {
267    pub method: HttpMethod,
268    pub payload: Option<HttpPayload>,
269    pub responses: HttpResponses,
270}
271
272impl Display for ApiSpec {
273    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
274        let mut result = format!("method: {}\n", self.method);
275        if let Some(payload) = &self.payload {
276            result.push_str(&format!("\t{}\n", payload));
277        }
278        if let Some(responses) = &self.responses {
279            result.push_str(&format!("\t{:?}", responses));
280        }
281        f.write_str(&result)
282    }
283}
284
285pub type HttpResponses = Option<HashMap<StatusCode, TypeDecl>>;
286
287#[derive(Eq, Hash, PartialEq, Clone, Debug)]
288pub enum StatusCode {
289    Fixed(u16),
290    Prefix(u16),
291}
292
293impl StatusCode {
294    pub fn as_key(&self) -> String {
295        match self {
296            StatusCode::Fixed(val) => val.to_string(),
297            StatusCode::Prefix(val) => val.to_string() + "xx",
298        }
299    }
300}
301
302impl Display for StatusCode {
303    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
304        match self {
305            StatusCode::Fixed(val) => f.write_str(&val.to_string()),
306            StatusCode::Prefix(val) => f.write_str(&(val.to_string() + "xx")),
307        }
308    }
309}
310
311#[derive(Debug, PartialEq)]
312pub enum HttpMethod {
313    Get,
314    Post,
315    Put,
316    Delete,
317    Patch,
318    Head,
319}
320
321impl Display for HttpMethod {
322    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
323        match self {
324            HttpMethod::Get => f.write_str("GET"),
325            HttpMethod::Post => f.write_str("POST"),
326            HttpMethod::Put => f.write_str("PUT"),
327            HttpMethod::Delete => f.write_str("DELETE"),
328            HttpMethod::Patch => f.write_str("PATCH"),
329            HttpMethod::Head => f.write_str("HEAD"),
330        }
331    }
332}
333
334#[derive(Debug, PartialEq)]
335pub enum HttpPayload {
336    Query(Vec<PropertyDecl>),
337    Body(Vec<PropertyDecl>),
338}
339
340impl Display for HttpPayload {
341    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
342        match self {
343            HttpPayload::Query(query) => {
344                let mut result = "Query: {\n".to_string();
345                for property_decl in query {
346                    result.push_str(&format!(
347                        "    {}: {:?}\n",
348                        property_decl.name, property_decl.data_type_decl
349                    ));
350                }
351                result.push_str("}\n");
352                f.write_str(&result)
353            }
354            HttpPayload::Body(body) => {
355                let mut result = "Body: {\n".to_string();
356                for property_decl in body {
357                    result.push_str(&format!(
358                        "    {}: {:?}\n",
359                        property_decl.name, property_decl.data_type_decl
360                    ));
361                }
362                result.push_str("}\n");
363                f.write_str(&result)
364            }
365        }
366    }
367}
368
369#[derive(Debug, PartialEq)]
370pub enum InterfaceDeclError {
371    ImportFailure(ImportError),
372    BodyNotAllowed,
373    QueryNotAllowed,
374    InvalidKey,
375    InvalidStatusCode,
376    TypeNotFound(String),
377    InvalidResponseDeclaration,
378    InvalidInterfaceDeclaration,
379    InvalidIdent,
380    EmptyParam,
381    InvalidMethod,
382    InvalidQuery,
383    InvalidBody,
384    InvalidResponseTypeDeclaration,
385}
386
387impl Error for InterfaceDeclError {}
388
389impl Display for InterfaceDeclError {
390    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
391        match self {
392            _ => f.write_str("InterfaceDeclError"),
393        }
394    }
395}
396
397pub type TypeUsageMeta = Option<Vec<UnknownType>>;
398
399#[derive(Debug, PartialEq, Clone)]
400pub enum UnknownType {
401    InTypeDeclaration(usize, usize),
402    InPayload(usize, usize),
403    InResponse(usize, StatusCode, usize)
404}
405