feat(codegen): implemented oneOf unions for primitive types

This commit is contained in:
pyrite 2025-05-27 19:23:22 +02:00
parent 549654f138
commit 70fb568230
Signed by: pyrite
GPG key ID: 7F1BA9170CD35D15
8 changed files with 380 additions and 129 deletions

View file

@ -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<Self> {
pub fn from_schema(name: &str, schema: &OpenApiType) -> Option<Self> {
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<crate::models::#ty> }
}
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<chrono::Utc> },
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::<Vec<_>>().join(","))
},
_ => quote! {
write!(f, "{}", value)
},
}
}
}
@ -77,12 +159,32 @@ impl Default for EnumVariantValue {
}
impl EnumVariantValue {
pub fn codegen_display(&self, name: &str) -> Option<TokenStream> {
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<TokenStream> {
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<Ident>,
elements: Vec<TokenStream>,
top_level_elements: Vec<TokenStream>,
}
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<TokenStream> {
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<TokenStream> {
pub fn codegen(
&self,
ns: &mut EnumNamespace,
resolved: &ResolvedSchema,
) -> Option<TokenStream> {
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<TokenStream> {
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<String>,
pub repr: Option<EnumRepr>,
pub copy: bool,
pub display: bool,
pub untagged: bool,
pub variants: Vec<EnumVariant>,
}
@ -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<TokenStream> {
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<TokenStream> {
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));
}
}

View file

@ -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<String, Model>,
pub paths: IndexMap<String, Path>,
pub parameters: Vec<Parameter>,
}
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<TokenStream> {
pub fn codegen(&self, resolved: &ResolvedSchema) -> Option<TokenStream> {
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,
}
}

View file

@ -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<TokenStream> {
pub fn codegen(
&self,
namespace: &mut ObjectNamespace,
resolved: &ResolvedSchema,
) -> Option<TokenStream> {
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<TokenStream> {
pub fn codegen(
&self,
namespace: &mut ObjectNamespace,
resolved: &ResolvedSchema,
) -> Option<TokenStream> {
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<TokenStream> {
pub fn codegen(&self, resolved: &ResolvedSchema) -> Option<TokenStream> {
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();

View file

@ -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<P> {
@ -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<TokenStream> {
pub fn codegen(&self, resolved: &ResolvedSchema) -> Option<TokenStream> {
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<T> From<T> for #name where T: IntoIterator<Item = #inner_ty> {
impl<T> From<T> 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));
}
}

View file

@ -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<TokenStream> {
pub fn codegen_request(&self, resolved: &ResolvedSchema) -> Option<TokenStream> {
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(&param.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);
}
}

View file

@ -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<i32>,
pub minimum: Option<i32>,
pub items: Option<Box<OpenApiParameterSchema<'a>>>,
pub one_of: Option<Vec<OpenApiType<'a>>>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]

View file

@ -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(&params_file);
fs::write(&params_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();
}

View file

@ -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]