torn_api_codegen/model/
mod.rs

1use r#enum::Enum;
2use indexmap::IndexMap;
3use newtype::Newtype;
4use object::Object;
5use proc_macro2::TokenStream;
6
7use crate::openapi::r#type::OpenApiType;
8
9pub mod r#enum;
10pub mod newtype;
11pub mod object;
12pub mod parameter;
13pub mod path;
14pub mod scope;
15pub mod union;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum Model {
19    Newtype(Newtype),
20    Enum(Enum),
21    Object(Object),
22    Unresolved,
23}
24
25pub fn resolve(r#type: &OpenApiType, name: &str, schemas: &IndexMap<&str, OpenApiType>) -> Model {
26    match r#type {
27        OpenApiType {
28            r#enum: Some(_), ..
29        } => Enum::from_schema(name, r#type).map_or(Model::Unresolved, Model::Enum),
30        OpenApiType {
31            r#type: Some("object"),
32            ..
33        } => Object::from_schema_object(name, r#type, schemas)
34            .map_or(Model::Unresolved, Model::Object),
35        OpenApiType {
36            r#type: Some(_), ..
37        } => Newtype::from_schema(name, r#type).map_or(Model::Unresolved, Model::Newtype),
38        OpenApiType {
39            one_of: Some(types),
40            ..
41        } => Enum::from_one_of(name, types).map_or(Model::Unresolved, Model::Enum),
42        OpenApiType {
43            all_of: Some(types),
44            ..
45        } => Object::from_all_of(name, types, schemas).map_or(Model::Unresolved, Model::Object),
46        _ => Model::Unresolved,
47    }
48}
49
50impl Model {
51    pub fn codegen(&self) -> Option<TokenStream> {
52        match self {
53            Self::Newtype(newtype) => newtype.codegen(),
54            Self::Enum(r#enum) => r#enum.codegen(),
55            Self::Object(object) => object.codegen(),
56            Self::Unresolved => None,
57        }
58    }
59}
60
61#[cfg(test)]
62mod test {
63    use super::*;
64    use crate::{
65        model::r#enum::{EnumRepr, EnumVariant},
66        openapi::schema::OpenApiSchema,
67    };
68
69    #[test]
70    fn resolve_newtypes() {
71        let schema = OpenApiSchema::read().unwrap();
72
73        let user_id_schema = schema.components.schemas.get("UserId").unwrap();
74
75        let user_id = resolve(user_id_schema, "UserId", &schema.components.schemas);
76
77        assert_eq!(
78            user_id,
79            Model::Newtype(Newtype {
80                name: "UserId".to_owned(),
81                description: None,
82                inner: newtype::NewtypeInner::I32,
83                copy: true,
84                ord: true
85            })
86        );
87
88        let attack_code_schema = schema.components.schemas.get("AttackCode").unwrap();
89
90        let attack_code = resolve(attack_code_schema, "AttackCode", &schema.components.schemas);
91
92        assert_eq!(
93            attack_code,
94            Model::Newtype(Newtype {
95                name: "AttackCode".to_owned(),
96                description: None,
97                inner: newtype::NewtypeInner::Str,
98                copy: false,
99                ord: false
100            })
101        );
102    }
103
104    #[test]
105    fn resolve_enums() {
106        let schema = OpenApiSchema::read().unwrap();
107
108        let forum_feed_type_schema = schema.components.schemas.get("ForumFeedTypeEnum").unwrap();
109
110        let forum_feed_type = resolve(
111            forum_feed_type_schema,
112            "ForumFeedTypeEnum",
113            &schema.components.schemas,
114        );
115
116        assert_eq!(forum_feed_type, Model::Enum(Enum {
117            name: "ForumFeedType".to_owned(),
118            description: Some("This represents the type of the activity. Values range from 1 to 8 where:\n *                    1 = 'X posted on a thread',\n *                    2 = 'X created a thread',\n *                    3 = 'X liked your thread',\n *                    4 = 'X disliked your thread',\n *                    5 = 'X liked your post',\n *                    6 = 'X disliked your post',\n *                    7 = 'X quoted your post'.".to_owned()),
119            repr: Some(EnumRepr::U32),
120            copy: true,
121            untagged: false,
122            display: true,
123            variants: vec![
124                EnumVariant {
125                    name: "Variant1".to_owned(),
126                    value: r#enum::EnumVariantValue::Repr(1),
127                    ..Default::default()
128                },
129                EnumVariant {
130                    name: "Variant2".to_owned(),
131                    value: r#enum::EnumVariantValue::Repr(2),
132                    ..Default::default()
133                },
134                EnumVariant {
135                    name: "Variant3".to_owned(),
136                    value: r#enum::EnumVariantValue::Repr(3),
137                    ..Default::default()
138                },
139                EnumVariant {
140                    name: "Variant4".to_owned(),
141                    value: r#enum::EnumVariantValue::Repr(4),
142                    ..Default::default()
143                },
144                EnumVariant {
145                    name: "Variant5".to_owned(),
146                    value: r#enum::EnumVariantValue::Repr(5),
147                    ..Default::default()
148                },
149                EnumVariant {
150                    name: "Variant6".to_owned(),
151                    value: r#enum::EnumVariantValue::Repr(6),
152                    ..Default::default()
153                },
154                EnumVariant {
155                    name: "Variant7".to_owned(),
156                    value: r#enum::EnumVariantValue::Repr(7),
157                    ..Default::default()
158                },
159            ]
160        }))
161    }
162
163    #[test]
164    fn resolve_all() {
165        let schema = OpenApiSchema::read().unwrap();
166
167        let mut unresolved = vec![];
168        let total = schema.components.schemas.len();
169
170        for (name, desc) in &schema.components.schemas {
171            if resolve(desc, name, &schema.components.schemas) == Model::Unresolved {
172                unresolved.push(name);
173            }
174        }
175
176        if !unresolved.is_empty() {
177            panic!(
178                "Failed to resolve {}/{} types. Could not resolve [{}]",
179                unresolved.len(),
180                total,
181                unresolved
182                    .into_iter()
183                    .map(|u| format!("`{u}`"))
184                    .collect::<Vec<_>>()
185                    .join(", ")
186            )
187        }
188    }
189}