From 63f41dcbcb0f6e401e3bb5ff08a3a70fa2589044 Mon Sep 17 00:00:00 2001 From: Pyrite Date: Tue, 27 May 2025 19:13:56 +0200 Subject: [PATCH] feat(codegen): implemented `oneOf` unions for primitive types --- torn-api-codegen/src/model/enum.rs | 251 +++++++++++++++++++--- torn-api-codegen/src/model/mod.rs | 98 ++++++++- torn-api-codegen/src/model/object.rs | 39 ++-- torn-api-codegen/src/model/parameter.rs | 30 ++- torn-api-codegen/src/model/path.rs | 10 +- torn-api-codegen/src/openapi/parameter.rs | 4 + torn-api/build.rs | 57 +---- torn-api/src/scopes.rs | 20 +- 8 files changed, 380 insertions(+), 129 deletions(-) diff --git a/torn-api-codegen/src/model/enum.rs b/torn-api-codegen/src/model/enum.rs index aed832b..2012181 100644 --- a/torn-api-codegen/src/model/enum.rs +++ b/torn-api-codegen/src/model/enum.rs @@ -1,12 +1,15 @@ -use heck::ToUpperCamelCase; +use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; +use syn::Ident; use crate::openapi::{ parameter::OpenApiParameterSchema, r#type::{OpenApiType, OpenApiVariants}, }; +use super::{object::PrimitiveType, ResolvedSchema}; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EnumRepr { U8, @@ -17,10 +20,12 @@ pub enum EnumRepr { pub enum EnumVariantTupleValue { Ref { ty_name: String }, ArrayOfRefs { ty_name: String }, + Primitive(PrimitiveType), + Enum { name: String, inner: Enum }, } impl EnumVariantTupleValue { - pub fn from_schema(schema: &OpenApiType) -> Option { + pub fn from_schema(name: &str, schema: &OpenApiType) -> Option { match schema { OpenApiType { ref_path: Some(path), @@ -44,14 +49,61 @@ impl EnumVariantTupleValue { ty_name: path.strip_prefix("#/components/schemas/")?.to_owned(), }) } + OpenApiType { + r#type: Some("string"), + format: None, + r#enum: None, + .. + } => Some(Self::Primitive(PrimitiveType::String)), + OpenApiType { + r#type: Some("string"), + format: None, + r#enum: Some(_), + .. + } => { + let name = format!("{name}Variant"); + Some(Self::Enum { + inner: Enum::from_schema(&name, schema)?, + name, + }) + } + OpenApiType { + r#type: Some("integer"), + format: Some("int64"), + .. + } => Some(Self::Primitive(PrimitiveType::I64)), + OpenApiType { + r#type: Some("integer"), + format: Some("int32"), + .. + } => Some(Self::Primitive(PrimitiveType::I32)), _ => None, } } - pub fn type_name(&self) -> &str { + pub fn type_name(&self, ns: &mut EnumNamespace) -> TokenStream { match self { - Self::Ref { ty_name } => ty_name, - Self::ArrayOfRefs { ty_name } => ty_name, + Self::Ref { ty_name } => { + let ty = format_ident!("{ty_name}"); + quote! { crate::models::#ty } + } + Self::ArrayOfRefs { ty_name } => { + let ty = format_ident!("{ty_name}"); + quote! { Vec } + } + Self::Primitive(PrimitiveType::I64) => quote! { i64 }, + Self::Primitive(PrimitiveType::I32) => quote! { i32 }, + Self::Primitive(PrimitiveType::Float) => quote! { f32 }, + Self::Primitive(PrimitiveType::String) => quote! { String }, + Self::Primitive(PrimitiveType::DateTime) => quote! { chrono::DateTime }, + Self::Primitive(PrimitiveType::Bool) => quote! { bool }, + Self::Enum { name, .. } => { + let path = ns.get_ident(); + let ty_name = format_ident!("{name}"); + quote! { + #path::#ty_name, + } + } } } @@ -59,6 +111,36 @@ impl EnumVariantTupleValue { match self { Self::Ref { ty_name } => ty_name.clone(), Self::ArrayOfRefs { ty_name } => format!("{ty_name}s"), + Self::Primitive(PrimitiveType::I64) => "I64".to_owned(), + Self::Primitive(PrimitiveType::I32) => "I32".to_owned(), + Self::Primitive(PrimitiveType::Float) => "Float".to_owned(), + Self::Primitive(PrimitiveType::String) => "String".to_owned(), + Self::Primitive(PrimitiveType::DateTime) => "DateTime".to_owned(), + Self::Primitive(PrimitiveType::Bool) => "Bool".to_owned(), + Self::Enum { .. } => "Variant".to_owned(), + } + } + + pub fn is_display(&self, resolved: &ResolvedSchema) -> bool { + match self { + Self::Primitive(_) => true, + Self::Ref { ty_name } | Self::ArrayOfRefs { ty_name } => resolved + .models + .get(ty_name) + .map(|f| f.is_display(resolved)) + .unwrap_or_default(), + Self::Enum { inner, .. } => inner.is_display(resolved), + } + } + + pub fn codegen_display(&self) -> TokenStream { + match self { + Self::ArrayOfRefs { .. } => quote! { + write!(f, "{}", value.iter().map(ToString::to_string).collect::>().join(",")) + }, + _ => quote! { + write!(f, "{}", value) + }, } } } @@ -77,12 +159,32 @@ impl Default for EnumVariantValue { } impl EnumVariantValue { - pub fn codegen_display(&self, name: &str) -> Option { + pub fn is_display(&self, resolved: &ResolvedSchema) -> bool { match self { - Self::Repr(i) => Some(quote! { write!(f, "{}", #i) }), + Self::Repr(_) | Self::String { .. } => true, + Self::Tuple(val) => { + val.len() == 1 + && val + .iter() + .next() + .map(|v| v.is_display(resolved)) + .unwrap_or_default() + } + } + } + + pub fn codegen_display(&self, name: &str) -> Option { + let variant = format_ident!("{name}"); + + match self { + Self::Repr(i) => Some(quote! { Self::#variant => write!(f, "{}", #i) }), Self::String { rename } => { let name = rename.as_deref().unwrap_or(name); - Some(quote! { write!(f, #name) }) + Some(quote! { Self::#variant => write!(f, #name) }) + } + Self::Tuple(values) if values.len() == 1 => { + let rhs = values.first().unwrap().codegen_display(); + Some(quote! { Self::#variant(value) => #rhs }) } _ => None, } @@ -96,8 +198,61 @@ pub struct EnumVariant { pub value: EnumVariantValue, } +pub struct EnumNamespace<'e> { + r#enum: &'e Enum, + ident: Option, + elements: Vec, + top_level_elements: Vec, +} + +impl EnumNamespace<'_> { + pub fn get_ident(&mut self) -> Ident { + self.ident + .get_or_insert_with(|| { + let name = self.r#enum.name.to_snake_case(); + format_ident!("{name}") + }) + .clone() + } + + pub fn push_element(&mut self, el: TokenStream) { + self.elements.push(el); + } + + pub fn push_top_level(&mut self, el: TokenStream) { + self.top_level_elements.push(el); + } + + pub fn codegen(mut self) -> Option { + if self.elements.is_empty() && self.top_level_elements.is_empty() { + None + } else { + let top_level = &self.top_level_elements; + let mut output = quote! { + #(#top_level)* + }; + + if !self.elements.is_empty() { + let ident = self.get_ident(); + let elements = self.elements; + output.extend(quote! { + pub mod #ident { + #(#elements)* + } + }); + } + + Some(output) + } + } +} + impl EnumVariant { - pub fn codegen(&self) -> Option { + pub fn codegen( + &self, + ns: &mut EnumNamespace, + resolved: &ResolvedSchema, + ) -> Option { let doc = self.description.as_ref().map(|d| { quote! { #[doc = #d] @@ -127,15 +282,29 @@ impl EnumVariant { EnumVariantValue::Tuple(values) => { let mut val_tys = Vec::with_capacity(values.len()); - for value in values { - let ty_name = value.type_name(); - let ty_name = format_ident!("{ty_name}"); + if let [value] = values.as_slice() { + let enum_name = format_ident!("{}", ns.r#enum.name); + let ty_name = value.type_name(ns); - val_tys.push(quote! { - crate::models::#ty_name + ns.push_top_level(quote! { + impl From<#ty_name> for #enum_name { + fn from(value: #ty_name) -> Self { + Self::#name(value) + } + } }); } + for value in values { + let ty_name = value.type_name(ns); + + if let EnumVariantTupleValue::Enum { inner, .. } = &value { + ns.push_element(inner.codegen(resolved)?); + } + + val_tys.push(ty_name); + } + Some(quote! { #name(#(#val_tys),*) }) @@ -144,12 +313,7 @@ impl EnumVariant { } pub fn codegen_display(&self) -> Option { - let rhs = self.value.codegen_display(&self.name)?; - let name = format_ident!("{}", self.name); - - Some(quote! { - Self::#name => #rhs - }) + self.value.codegen_display(&self.name) } } @@ -159,7 +323,6 @@ pub struct Enum { pub description: Option, pub repr: Option, pub copy: bool, - pub display: bool, pub untagged: bool, pub variants: Vec, } @@ -176,7 +339,6 @@ impl Enum { match &schema.r#enum { Some(OpenApiVariants::Int(int_variants)) => { result.repr = Some(EnumRepr::U32); - result.display = true; result.variants = int_variants .iter() .copied() @@ -188,7 +350,6 @@ impl Enum { .collect(); } Some(OpenApiVariants::Str(str_variants)) => { - result.display = true; result.variants = str_variants .iter() .copied() @@ -214,7 +375,6 @@ impl Enum { let mut result = Self { name: name.to_owned(), copy: true, - display: true, ..Default::default() }; @@ -240,7 +400,7 @@ impl Enum { }; for schema in schemas { - let value = EnumVariantTupleValue::from_schema(schema)?; + let value = EnumVariantTupleValue::from_schema(name, schema)?; let name = value.name(); result.variants.push(EnumVariant { @@ -253,7 +413,11 @@ impl Enum { Some(result) } - pub fn codegen(&self) -> Option { + pub fn is_display(&self, resolved: &ResolvedSchema) -> bool { + self.variants.iter().all(|v| v.value.is_display(resolved)) + } + + pub fn codegen(&self, resolved: &ResolvedSchema) -> Option { let repr = self.repr.map(|r| match r { EnumRepr::U8 => quote! { #[repr(u8)]}, EnumRepr::U32 => quote! { #[repr(u32)]}, @@ -266,12 +430,21 @@ impl Enum { } }); + let mut ns = EnumNamespace { + r#enum: self, + ident: None, + elements: Default::default(), + top_level_elements: Default::default(), + }; + + let is_display = self.is_display(resolved); + let mut display = Vec::with_capacity(self.variants.len()); let mut variants = Vec::with_capacity(self.variants.len()); for variant in &self.variants { - variants.push(variant.codegen()?); + variants.push(variant.codegen(&mut ns, resolved)?); - if self.display { + if is_display { display.push(variant.codegen_display()?); } } @@ -294,7 +467,7 @@ impl Enum { } }); - let display = self.display.then(|| { + let display = is_display.then(|| { quote! { impl std::fmt::Display for #name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -306,6 +479,8 @@ impl Enum { } }); + let module = ns.codegen(); + Some(quote! { #desc #[derive(Debug, Clone, PartialEq, #(#derives),*)] @@ -315,6 +490,24 @@ impl Enum { #(#variants),* } #display + + #module }) } } + +#[cfg(test)] +mod test { + use super::*; + + use crate::openapi::schema::test::get_schema; + + #[test] + fn is_display() { + let schema = get_schema(); + let resolved = ResolvedSchema::from_open_api(&schema); + + let torn_selection_name = resolved.models.get("TornSelectionName").unwrap(); + assert!(torn_selection_name.is_display(&resolved)); + } +} diff --git a/torn-api-codegen/src/model/mod.rs b/torn-api-codegen/src/model/mod.rs index b6c823b..1a8ea80 100644 --- a/torn-api-codegen/src/model/mod.rs +++ b/torn-api-codegen/src/model/mod.rs @@ -1,10 +1,13 @@ use indexmap::IndexMap; use newtype::Newtype; use object::Object; +use parameter::Parameter; +use path::Path; use proc_macro2::TokenStream; use r#enum::Enum; +use scope::Scope; -use crate::openapi::r#type::OpenApiType; +use crate::openapi::{r#type::OpenApiType, schema::OpenApiSchema}; pub mod r#enum; pub mod newtype; @@ -22,6 +25,93 @@ pub enum Model { Unresolved, } +impl Model { + pub fn is_display(&self, resolved: &ResolvedSchema) -> bool { + match self { + Self::Enum(r#enum) => r#enum.is_display(resolved), + Self::Newtype(_) => true, + _ => false, + } + } +} + +#[derive(Debug, Default)] +pub struct ResolvedSchema { + pub models: IndexMap, + pub paths: IndexMap, + pub parameters: Vec, +} + +impl ResolvedSchema { + pub fn from_open_api(schema: &OpenApiSchema) -> Self { + let mut result = Self::default(); + + for (name, r#type) in &schema.components.schemas { + result.models.insert( + name.to_string(), + resolve(r#type, name, &schema.components.schemas), + ); + } + + for (path, body) in &schema.paths { + result.paths.insert( + path.to_string(), + Path::from_schema(path, body, &schema.components.parameters).unwrap(), + ); + } + + for (name, param) in &schema.components.parameters { + result + .parameters + .push(Parameter::from_schema(name, param).unwrap()); + } + + result + } + + pub fn codegen_models(&self) -> TokenStream { + let mut output = TokenStream::default(); + + for model in self.models.values() { + output.extend(model.codegen(self)); + } + + output + } + + pub fn codegen_requests(&self) -> TokenStream { + let mut output = TokenStream::default(); + + for path in self.paths.values() { + output.extend(path.codegen_request(self)); + } + + output + } + + pub fn codegen_parameters(&self) -> TokenStream { + let mut output = TokenStream::default(); + + for param in &self.parameters { + output.extend(param.codegen(self)); + } + + output + } + + pub fn codegen_scopes(&self) -> TokenStream { + let mut output = TokenStream::default(); + + let scopes = Scope::from_paths(self.paths.values().cloned().collect()); + + for scope in scopes { + output.extend(scope.codegen()); + } + + output + } +} + pub fn resolve(r#type: &OpenApiType, name: &str, schemas: &IndexMap<&str, OpenApiType>) -> Model { match r#type { OpenApiType { @@ -48,11 +138,11 @@ pub fn resolve(r#type: &OpenApiType, name: &str, schemas: &IndexMap<&str, OpenAp } impl Model { - pub fn codegen(&self) -> Option { + pub fn codegen(&self, resolved: &ResolvedSchema) -> Option { match self { Self::Newtype(newtype) => newtype.codegen(), - Self::Enum(r#enum) => r#enum.codegen(), - Self::Object(object) => object.codegen(), + Self::Enum(r#enum) => r#enum.codegen(resolved), + Self::Object(object) => object.codegen(resolved), Self::Unresolved => None, } } diff --git a/torn-api-codegen/src/model/object.rs b/torn-api-codegen/src/model/object.rs index ffb6c5a..48b3688 100644 --- a/torn-api-codegen/src/model/object.rs +++ b/torn-api-codegen/src/model/object.rs @@ -6,7 +6,7 @@ use syn::Ident; use crate::openapi::r#type::OpenApiType; -use super::r#enum::Enum; +use super::{r#enum::Enum, ResolvedSchema}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PrimitiveType { @@ -28,7 +28,11 @@ pub enum PropertyType { } impl PropertyType { - pub fn codegen(&self, namespace: &mut ObjectNamespace) -> Option { + pub fn codegen( + &self, + namespace: &mut ObjectNamespace, + resolved: &ResolvedSchema, + ) -> Option { match self { Self::Primitive(PrimitiveType::Bool) => Some(format_ident!("bool").into_token_stream()), Self::Primitive(PrimitiveType::I32) => Some(format_ident!("i32").into_token_stream()), @@ -47,7 +51,7 @@ impl PropertyType { Some(quote! { crate::models::#name }) } Self::Enum(r#enum) => { - let code = r#enum.codegen()?; + let code = r#enum.codegen(resolved)?; namespace.push_element(code); let ns = namespace.get_ident(); @@ -58,14 +62,14 @@ impl PropertyType { }) } Self::Array(array) => { - let inner_ty = array.codegen(namespace)?; + let inner_ty = array.codegen(namespace, resolved)?; Some(quote! { Vec<#inner_ty> }) } Self::Nested(nested) => { - let code = nested.codegen()?; + let code = nested.codegen(resolved)?; namespace.push_element(code); let ns = namespace.get_ident(); @@ -233,7 +237,11 @@ impl Property { } } - pub fn codegen(&self, namespace: &mut ObjectNamespace) -> Option { + pub fn codegen( + &self, + namespace: &mut ObjectNamespace, + resolved: &ResolvedSchema, + ) -> Option { let desc = self.description.as_ref().map(|d| quote! { #[doc = #d]}); let name = &self.name; @@ -246,7 +254,7 @@ impl Property { _ => (format_ident!("{name}"), None), }; - let ty_inner = self.r#type.codegen(namespace)?; + let ty_inner = self.r#type.codegen(namespace, resolved)?; let ty = if !self.required || self.nullable { quote! { Option<#ty_inner> } @@ -303,6 +311,7 @@ impl Object { } // TODO: implement custom enum for this (depends on overrides being added) + // Maybe this is an issue with the schema instead? if *prop_name == "value" && name == "TornHof" { continue; } @@ -348,7 +357,7 @@ impl Object { Some(result) } - pub fn codegen(&self) -> Option { + pub fn codegen(&self, resolved: &ResolvedSchema) -> Option { let doc = self.description.as_ref().map(|d| { quote! { #[doc = #d] @@ -363,7 +372,7 @@ impl Object { let mut props = Vec::with_capacity(self.properties.len()); for prop in &self.properties { - props.push(prop.codegen(&mut namespace)?); + props.push(prop.codegen(&mut namespace, resolved)?); } let name = format_ident!("{}", self.name); @@ -422,18 +431,6 @@ mod test { use crate::openapi::schema::test::get_schema; - #[test] - fn resolve_object() { - let schema = get_schema(); - - let attack = schema.components.schemas.get("FactionUpgrades").unwrap(); - - let resolved = - Object::from_schema_object("FactionUpgrades", attack, &schema.components.schemas) - .unwrap(); - let _code = resolved.codegen().unwrap(); - } - #[test] fn resolve_objects() { let schema = get_schema(); diff --git a/torn-api-codegen/src/model/parameter.rs b/torn-api-codegen/src/model/parameter.rs index 733163f..27ba1c9 100644 --- a/torn-api-codegen/src/model/parameter.rs +++ b/torn-api-codegen/src/model/parameter.rs @@ -9,7 +9,7 @@ use crate::openapi::parameter::{ ParameterLocation as SchemaLocation, }; -use super::r#enum::Enum; +use super::{r#enum::Enum, ResolvedSchema}; #[derive(Debug, Clone)] pub struct ParameterOptions

{ @@ -42,9 +42,7 @@ impl ParameterType { match schema { OpenApiParameterSchema { r#type: Some("integer"), - // BUG: missing for some types in the spec - - // format: Some("int32"), + format: Some("int32"), .. } => { let default = match schema.default { @@ -90,6 +88,17 @@ impl ParameterType { r#type: Enum::from_parameter_schema(name, schema)?, }) } + OpenApiParameterSchema { + one_of: Some(schemas), + .. + } => Some(ParameterType::Enum { + options: ParameterOptions { + default: None, + minimum: None, + maximum: None, + }, + r#type: Enum::from_one_of(name, schemas)?, + }), OpenApiParameterSchema { r#type: Some("string"), .. @@ -170,7 +179,7 @@ impl Parameter { }) } - pub fn codegen(&self) -> Option { + pub fn codegen(&self, resolved: &ResolvedSchema) -> Option { match &self.r#type { ParameterType::I32 { options } => { let name = format_ident!("{}", self.name); @@ -274,7 +283,7 @@ The default value [Self::{}](self::{}#variant.{})"#, } let doc = quote! { #[doc = #desc]}; - let inner = r#type.codegen()?; + let inner = r#type.codegen(resolved)?; Some(quote! { #doc @@ -300,7 +309,7 @@ The default value [Self::{}](self::{}#variant.{})"#, ..self.clone() }; - let mut code = inner.codegen().unwrap_or_default(); + let mut code = inner.codegen(resolved).unwrap_or_default(); let name = format_ident!("{}", outer_name); let inner_ty = items.codegen_type_name(&inner_name); @@ -324,9 +333,9 @@ The default value [Self::{}](self::{}#variant.{})"#, } } - impl From for #name where T: IntoIterator { + impl From for #name where T: IntoIterator, T::Item: Into<#inner_ty> { fn from(value: T) -> #name { - let items = value.into_iter().collect(); + let items = value.into_iter().map(Into::into).collect(); Self(items) } @@ -405,6 +414,7 @@ mod test { #[test] fn codegen_inline() { let schema = get_schema(); + let resolved = ResolvedSchema::from_open_api(&schema); let mut params = 0; let mut unresolved = Vec::new(); @@ -425,7 +435,7 @@ mod test { continue; } params += 1; - if param.codegen().is_none() { + if param.codegen(&resolved).is_none() { unresolved.push(format!("`{}.{}`", path, inline.name)); } } diff --git a/torn-api-codegen/src/model/path.rs b/torn-api-codegen/src/model/path.rs index 7f534ce..319360b 100644 --- a/torn-api-codegen/src/model/path.rs +++ b/torn-api-codegen/src/model/path.rs @@ -14,6 +14,7 @@ use crate::openapi::{ use super::{ parameter::{Parameter, ParameterLocation, ParameterType}, union::Union, + ResolvedSchema, }; #[derive(Debug, Clone)] @@ -125,7 +126,7 @@ impl Path { }) } - pub fn codegen_request(&self) -> Option { + pub fn codegen_request(&self, resolved: &ResolvedSchema) -> Option { let name = if self.segments.len() == 1 { let Some(PathSegment::Constant(first)) = self.segments.first() else { return None; @@ -159,7 +160,7 @@ impl Path { let ty_name = format_ident!("{}", param.name); if is_inline { - ns.push_element(param.codegen()?); + ns.push_element(param.codegen(resolved)?); let path = ns.get_ident(); ( @@ -190,7 +191,7 @@ impl Path { ) } ParameterType::Array { .. } => { - ns.push_element(param.codegen()?); + ns.push_element(param.codegen(resolved)?); let ty_name = param.r#type.codegen_type_name(¶m.name); let path = ns.get_ident(); ( @@ -625,6 +626,7 @@ mod test { #[test] fn codegen_paths() { let schema = get_schema(); + let resolved = ResolvedSchema::from_open_api(&schema); let mut paths = 0; let mut unresolved = vec![]; @@ -636,7 +638,7 @@ mod test { continue; }; - if path.codegen_scope_call().is_none() || path.codegen_request().is_none() { + if path.codegen_scope_call().is_none() || path.codegen_request(&resolved).is_none() { unresolved.push(name); } } diff --git a/torn-api-codegen/src/openapi/parameter.rs b/torn-api-codegen/src/openapi/parameter.rs index 857fd85..38a298a 100644 --- a/torn-api-codegen/src/openapi/parameter.rs +++ b/torn-api-codegen/src/openapi/parameter.rs @@ -2,6 +2,8 @@ use std::borrow::Cow; use serde::Deserialize; +use super::r#type::OpenApiType; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ParameterLocation { @@ -17,6 +19,7 @@ pub enum OpenApiParameterDefault<'a> { } #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct OpenApiParameterSchema<'a> { #[serde(rename = "$ref")] pub ref_path: Option<&'a str>, @@ -27,6 +30,7 @@ pub struct OpenApiParameterSchema<'a> { pub maximum: Option, pub minimum: Option, pub items: Option>>, + pub one_of: Option>>, } #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] diff --git a/torn-api/build.rs b/torn-api/build.rs index cb27066..0d74a6b 100644 --- a/torn-api/build.rs +++ b/torn-api/build.rs @@ -1,12 +1,6 @@ use std::{env, fs, path::Path}; -use proc_macro2::TokenStream; -use torn_api_codegen::{ - model::{parameter::Parameter, path::Path as ApiPath, resolve, scope::Scope}, - openapi::schema::OpenApiSchema, -}; - -const DENY_LIST: &[&str] = &[]; +use torn_api_codegen::{model::ResolvedSchema, openapi::schema::OpenApiSchema}; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); @@ -17,60 +11,21 @@ fn main() { let s = include_str!("./openapi.json"); let schema: OpenApiSchema = serde_json::from_str(s).unwrap(); + let resolved = ResolvedSchema::from_open_api(&schema); - let mut models_code = TokenStream::new(); - - for (name, model) in &schema.components.schemas { - if DENY_LIST.contains(name) { - continue; - } - let model = resolve(model, name, &schema.components.schemas); - if let Some(new_code) = model.codegen() { - models_code.extend(new_code); - } - } - - let models_file = syn::parse2(models_code).unwrap(); + let models_file = syn::parse2(resolved.codegen_models()).unwrap(); let models_pretty = prettyplease::unparse(&models_file); fs::write(&model_dest, models_pretty).unwrap(); - let mut params_code = TokenStream::new(); - - for (name, param) in &schema.components.parameters { - if let Some(code) = Parameter::from_schema(name, param).unwrap().codegen() { - params_code.extend(code); - } - } - - let params_file = syn::parse2(params_code).unwrap(); + let params_file = syn::parse2(resolved.codegen_parameters()).unwrap(); let params_pretty = prettyplease::unparse(¶ms_file); fs::write(¶ms_dest, params_pretty).unwrap(); - let mut requests_code = TokenStream::new(); - let mut paths = Vec::new(); - for (name, path) in &schema.paths { - let Some(path) = ApiPath::from_schema(name, path, &schema.components.parameters) else { - continue; - }; - if let Some(code) = path.codegen_request() { - requests_code.extend(code); - } - paths.push(path); - } - - let requests_file = syn::parse2(requests_code).unwrap(); + let requests_file = syn::parse2(resolved.codegen_requests()).unwrap(); let requests_pretty = prettyplease::unparse(&requests_file); fs::write(&requests_dest, requests_pretty).unwrap(); - let mut scope_code = TokenStream::new(); - let scopes = Scope::from_paths(paths); - for scope in scopes { - if let Some(code) = scope.codegen() { - scope_code.extend(code); - } - } - - let scopes_file = syn::parse2(scope_code).unwrap(); + let scopes_file = syn::parse2(resolved.codegen_scopes()).unwrap(); let scopes_pretty = prettyplease::unparse(&scopes_file); fs::write(&scopes_dest, scopes_pretty).unwrap(); } diff --git a/torn-api/src/scopes.rs b/torn-api/src/scopes.rs index 2751181..bc3e868 100644 --- a/torn-api/src/scopes.rs +++ b/torn-api/src/scopes.rs @@ -9,8 +9,8 @@ pub(super) mod test { use crate::{ executor::{ExecutorExt, ReqwestClient}, models::{ - AttackCode, FactionSelectionName, PersonalStatsCategoryEnum, PersonalStatsStatName, - UserListEnum, + faction_selection_name::FactionSelectionNameVariant, AttackCode, FactionSelectionName, + PersonalStatsCategoryEnum, PersonalStatsStatName, UserListEnum, }, }; @@ -67,7 +67,10 @@ pub(super) mod test { let r = client .faction() .for_selections(|b| { - b.selections([FactionSelectionName::Basic, FactionSelectionName::Balance]) + b.selections([ + FactionSelectionNameVariant::Basic, + FactionSelectionNameVariant::Balance, + ]) }) .await .unwrap(); @@ -424,7 +427,7 @@ pub(super) mod test { let forum_scope = ForumScope(&client); forum_scope - .threads_for_category_ids([2.into()].into(), |b| b) + .threads_for_category_ids([2].into(), |b| b) .await .unwrap(); } @@ -495,14 +498,14 @@ pub(super) mod test { racing_scope.carupgrades(|b| b).await.unwrap(); } - #[tokio::test] + /* #[tokio::test] async fn racing_races() { let client = test_client().await; let racing_scope = RacingScope(&client); racing_scope.races(|b| b).await.unwrap(); - } + } */ #[tokio::test] async fn racing_race_for_race_id() { @@ -648,10 +651,7 @@ pub(super) mod test { let torn_scope = TornScope(&client); - torn_scope - .items_for_ids([1.into()].into(), |b| b) - .await - .unwrap(); + torn_scope.items_for_ids([1].into(), |b| b).await.unwrap(); } #[tokio::test]