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}