torn_api_codegen/model/
mod.rs1use indexmap::IndexMap;
2use newtype::Newtype;
3use object::Object;
4use parameter::Parameter;
5use path::Path;
6use proc_macro2::TokenStream;
7use r#enum::Enum;
8use scope::Scope;
9
10use crate::openapi::{r#type::OpenApiType, schema::OpenApiSchema};
11
12pub mod r#enum;
13pub mod newtype;
14pub mod object;
15pub mod parameter;
16pub mod path;
17pub mod scope;
18pub mod union;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum Model {
22 Newtype(Newtype),
23 Enum(Enum),
24 Object(Object),
25 Unresolved,
26}
27
28impl Model {
29 pub fn is_display(&self, resolved: &ResolvedSchema) -> bool {
30 match self {
31 Self::Enum(r#enum) => r#enum.is_display(resolved),
32 Self::Newtype(_) => true,
33 _ => false,
34 }
35 }
36}
37
38#[derive(Debug, Default)]
39pub struct ResolvedSchema {
40 pub models: IndexMap<String, Model>,
41 pub paths: IndexMap<String, Path>,
42 pub parameters: Vec<Parameter>,
43}
44
45impl ResolvedSchema {
46 pub fn from_open_api(schema: &OpenApiSchema) -> Self {
47 let mut result = Self::default();
48
49 for (name, r#type) in &schema.components.schemas {
50 result.models.insert(
51 name.to_string(),
52 resolve(r#type, name, &schema.components.schemas),
53 );
54 }
55
56 for (path, body) in &schema.paths {
57 result.paths.insert(
58 path.to_string(),
59 Path::from_schema(path, body, &schema.components.parameters).unwrap(),
60 );
61 }
62
63 for (name, param) in &schema.components.parameters {
64 result
65 .parameters
66 .push(Parameter::from_schema(name, param).unwrap());
67 }
68
69 result
70 }
71
72 pub fn codegen_models(&self) -> TokenStream {
73 let mut output = TokenStream::default();
74
75 for model in self.models.values() {
76 output.extend(model.codegen(self));
77 }
78
79 output
80 }
81
82 pub fn codegen_requests(&self) -> TokenStream {
83 let mut output = TokenStream::default();
84
85 for path in self.paths.values() {
86 output.extend(path.codegen_request(self));
87 }
88
89 output
90 }
91
92 pub fn codegen_parameters(&self) -> TokenStream {
93 let mut output = TokenStream::default();
94
95 for param in &self.parameters {
96 output.extend(param.codegen(self));
97 }
98
99 output
100 }
101
102 pub fn codegen_scopes(&self) -> TokenStream {
103 let mut output = TokenStream::default();
104
105 let scopes = Scope::from_paths(self.paths.values().cloned().collect());
106
107 for scope in scopes {
108 output.extend(scope.codegen());
109 }
110
111 output
112 }
113}
114
115pub fn resolve(r#type: &OpenApiType, name: &str, schemas: &IndexMap<&str, OpenApiType>) -> Model {
116 match r#type {
117 OpenApiType {
118 r#enum: Some(_), ..
119 } => Enum::from_schema(name, r#type).map_or(Model::Unresolved, Model::Enum),
120 OpenApiType {
121 r#type: Some("object"),
122 ..
123 } => Object::from_schema_object(name, r#type, schemas)
124 .map_or(Model::Unresolved, Model::Object),
125 OpenApiType {
126 r#type: Some(_), ..
127 } => Newtype::from_schema(name, r#type).map_or(Model::Unresolved, Model::Newtype),
128 OpenApiType {
129 one_of: Some(types),
130 ..
131 } => Enum::from_one_of(name, types).map_or(Model::Unresolved, Model::Enum),
132 OpenApiType {
133 all_of: Some(types),
134 ..
135 } => Object::from_all_of(name, types, schemas).map_or(Model::Unresolved, Model::Object),
136 _ => Model::Unresolved,
137 }
138}
139
140impl Model {
141 pub fn codegen(&self, resolved: &ResolvedSchema) -> Option<TokenStream> {
142 match self {
143 Self::Newtype(newtype) => newtype.codegen(),
144 Self::Enum(r#enum) => r#enum.codegen(resolved),
145 Self::Object(object) => object.codegen(resolved),
146 Self::Unresolved => None,
147 }
148 }
149}
150
151#[cfg(test)]
152mod test {
153 use super::*;
154 use crate::openapi::schema::test::get_schema;
155
156 #[test]
157 fn resolve_newtypes() {
158 let schema = get_schema();
159
160 let user_id_schema = schema.components.schemas.get("UserId").unwrap();
161
162 let user_id = resolve(user_id_schema, "UserId", &schema.components.schemas);
163
164 assert_eq!(
165 user_id,
166 Model::Newtype(Newtype {
167 name: "UserId".to_owned(),
168 description: None,
169 inner: newtype::NewtypeInner::I32,
170 copy: true,
171 ord: true
172 })
173 );
174
175 let attack_code_schema = schema.components.schemas.get("AttackCode").unwrap();
176
177 let attack_code = resolve(attack_code_schema, "AttackCode", &schema.components.schemas);
178
179 assert_eq!(
180 attack_code,
181 Model::Newtype(Newtype {
182 name: "AttackCode".to_owned(),
183 description: None,
184 inner: newtype::NewtypeInner::Str,
185 copy: false,
186 ord: false
187 })
188 );
189 }
190
191 #[test]
192 fn resolve_all() {
193 let schema = get_schema();
194
195 let mut unresolved = vec![];
196 let total = schema.components.schemas.len();
197
198 for (name, desc) in &schema.components.schemas {
199 if resolve(desc, name, &schema.components.schemas) == Model::Unresolved {
200 unresolved.push(name);
201 }
202 }
203
204 if !unresolved.is_empty() {
205 panic!(
206 "Failed to resolve {}/{} types. Could not resolve [{}]",
207 unresolved.len(),
208 total,
209 unresolved
210 .into_iter()
211 .map(|u| format!("`{u}`"))
212 .collect::<Vec<_>>()
213 .join(", ")
214 )
215 }
216 }
217}