Compare commits
13 commits
70fb568230
...
39731f2f5d
Author | SHA1 | Date | |
---|---|---|---|
|
39731f2f5d | ||
|
83dfdb27ac | ||
|
6aaa06f501 | ||
|
c8bdcc81c4 | ||
|
8bfa1b8ac3 | ||
|
56e64470de | ||
|
6d57f275a2 | ||
|
11c5d71bf6 | ||
|
eb6e98f41b | ||
|
7a4f6462f5 | ||
|
266122ea0e | ||
|
47461b61b2 | ||
|
b245e3e712 |
31
Cargo.lock
generated
31
Cargo.lock
generated
|
@ -227,6 +227,7 @@ dependencies = [
|
|||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
@ -2080,6 +2081,28 @@ version = "0.11.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
@ -2271,10 +2294,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "torn-api"
|
||||
version = "1.2.0"
|
||||
version = "1.5.0"
|
||||
dependencies = [
|
||||
"bon",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"futures",
|
||||
"http",
|
||||
"prettyplease",
|
||||
|
@ -2283,6 +2307,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"strum",
|
||||
"syn",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
@ -2291,7 +2316,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "torn-api-codegen"
|
||||
version = "0.2.3"
|
||||
version = "0.5.1"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"indexmap",
|
||||
|
@ -2304,7 +2329,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "torn-key-pool"
|
||||
version = "1.1.1"
|
||||
version = "1.1.3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "torn-api-codegen"
|
||||
authors = ["Pyrit [2111649]"]
|
||||
version = "0.2.3"
|
||||
version = "0.5.1"
|
||||
edition = "2021"
|
||||
description = "Contains the v2 torn API model descriptions and codegen for the bindings"
|
||||
license-file = { workspace = true }
|
||||
|
|
|
@ -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,34 +479,35 @@ impl Enum {
|
|||
}
|
||||
});
|
||||
|
||||
let module = ns.codegen();
|
||||
|
||||
Some(quote! {
|
||||
#desc
|
||||
#[derive(Debug, Clone, PartialEq, #(#derives),*)]
|
||||
#[cfg_attr(feature = "strum", derive(strum::EnumIs, strum::EnumTryAs))]
|
||||
#serde_attr
|
||||
pub enum #name {
|
||||
#(#variants),*
|
||||
}
|
||||
#display
|
||||
|
||||
#module
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::openapi::schema::OpenApiSchema;
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::openapi::schema::test::get_schema;
|
||||
|
||||
#[test]
|
||||
fn codegen() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
fn is_display() {
|
||||
let schema = get_schema();
|
||||
let resolved = ResolvedSchema::from_open_api(&schema);
|
||||
|
||||
let revive_setting = schema.components.schemas.get("ReviveSetting").unwrap();
|
||||
|
||||
let r#enum = Enum::from_schema("ReviveSetting", revive_setting).unwrap();
|
||||
|
||||
let code = r#enum.codegen().unwrap();
|
||||
|
||||
panic!("{code}");
|
||||
let torn_selection_name = resolved.models.get("TornSelectionName").unwrap();
|
||||
assert!(torn_selection_name.is_display(&resolved));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use r#enum::Enum;
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -61,14 +151,11 @@ impl Model {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
model::r#enum::{EnumRepr, EnumVariant},
|
||||
openapi::schema::OpenApiSchema,
|
||||
};
|
||||
use crate::openapi::schema::test::get_schema;
|
||||
|
||||
#[test]
|
||||
fn resolve_newtypes() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
let schema = get_schema();
|
||||
|
||||
let user_id_schema = schema.components.schemas.get("UserId").unwrap();
|
||||
|
||||
|
@ -101,68 +188,9 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_enums() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
|
||||
let forum_feed_type_schema = schema.components.schemas.get("ForumFeedTypeEnum").unwrap();
|
||||
|
||||
let forum_feed_type = resolve(
|
||||
forum_feed_type_schema,
|
||||
"ForumFeedTypeEnum",
|
||||
&schema.components.schemas,
|
||||
);
|
||||
|
||||
assert_eq!(forum_feed_type, Model::Enum(Enum {
|
||||
name: "ForumFeedType".to_owned(),
|
||||
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()),
|
||||
repr: Some(EnumRepr::U32),
|
||||
copy: true,
|
||||
untagged: false,
|
||||
display: true,
|
||||
variants: vec![
|
||||
EnumVariant {
|
||||
name: "Variant1".to_owned(),
|
||||
value: r#enum::EnumVariantValue::Repr(1),
|
||||
..Default::default()
|
||||
},
|
||||
EnumVariant {
|
||||
name: "Variant2".to_owned(),
|
||||
value: r#enum::EnumVariantValue::Repr(2),
|
||||
..Default::default()
|
||||
},
|
||||
EnumVariant {
|
||||
name: "Variant3".to_owned(),
|
||||
value: r#enum::EnumVariantValue::Repr(3),
|
||||
..Default::default()
|
||||
},
|
||||
EnumVariant {
|
||||
name: "Variant4".to_owned(),
|
||||
value: r#enum::EnumVariantValue::Repr(4),
|
||||
..Default::default()
|
||||
},
|
||||
EnumVariant {
|
||||
name: "Variant5".to_owned(),
|
||||
value: r#enum::EnumVariantValue::Repr(5),
|
||||
..Default::default()
|
||||
},
|
||||
EnumVariant {
|
||||
name: "Variant6".to_owned(),
|
||||
value: r#enum::EnumVariantValue::Repr(6),
|
||||
..Default::default()
|
||||
},
|
||||
EnumVariant {
|
||||
name: "Variant7".to_owned(),
|
||||
value: r#enum::EnumVariantValue::Repr(7),
|
||||
..Default::default()
|
||||
},
|
||||
]
|
||||
}))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_all() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
let schema = get_schema();
|
||||
|
||||
let mut unresolved = vec![];
|
||||
let total = schema.components.schemas.len();
|
||||
|
|
|
@ -121,24 +121,3 @@ impl Newtype {
|
|||
Some(body)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::openapi::schema::OpenApiSchema;
|
||||
|
||||
#[test]
|
||||
fn codegen() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
|
||||
let user_id = schema.components.schemas.get("UserId").unwrap();
|
||||
|
||||
let mut newtype = Newtype::from_schema("UserId", user_id).unwrap();
|
||||
|
||||
newtype.description = Some("Description goes here".to_owned());
|
||||
|
||||
let code = newtype.codegen().unwrap().to_string();
|
||||
|
||||
panic!("{code}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -15,6 +15,7 @@ pub enum PrimitiveType {
|
|||
I64,
|
||||
String,
|
||||
Float,
|
||||
DateTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -27,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()),
|
||||
|
@ -35,6 +40,9 @@ impl PropertyType {
|
|||
Self::Primitive(PrimitiveType::String) => {
|
||||
Some(format_ident!("String").into_token_stream())
|
||||
}
|
||||
Self::Primitive(PrimitiveType::DateTime) => {
|
||||
Some(quote! { chrono::DateTime<chrono::Utc> })
|
||||
}
|
||||
Self::Primitive(PrimitiveType::Float) => Some(format_ident!("f64").into_token_stream()),
|
||||
Self::Ref(path) => {
|
||||
let name = path.strip_prefix("#/components/schemas/")?;
|
||||
|
@ -43,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();
|
||||
|
@ -54,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();
|
||||
|
@ -207,8 +215,11 @@ impl Property {
|
|||
let prim = match (schema.r#type, schema.format) {
|
||||
(Some("integer"), Some("int32")) => PrimitiveType::I32,
|
||||
(Some("integer"), Some("int64")) => PrimitiveType::I64,
|
||||
(Some("number"), /* Some("float") */ _) => PrimitiveType::Float,
|
||||
(Some("number"), /* Some("float") */ _) | (_, Some("float")) => {
|
||||
PrimitiveType::Float
|
||||
}
|
||||
(Some("string"), None) => PrimitiveType::String,
|
||||
(Some("string"), Some("date")) => PrimitiveType::DateTime,
|
||||
(Some("boolean"), None) => PrimitiveType::Bool,
|
||||
_ => return None,
|
||||
};
|
||||
|
@ -226,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;
|
||||
|
@ -239,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> }
|
||||
|
@ -296,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;
|
||||
}
|
||||
|
@ -341,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]
|
||||
|
@ -356,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);
|
||||
|
@ -413,23 +429,11 @@ impl ObjectNamespace<'_> {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::openapi::schema::OpenApiSchema;
|
||||
|
||||
#[test]
|
||||
fn resolve_object() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
|
||||
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();
|
||||
}
|
||||
use crate::openapi::schema::test::get_schema;
|
||||
|
||||
#[test]
|
||||
fn resolve_objects() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
let schema = get_schema();
|
||||
|
||||
let mut objects = 0;
|
||||
let mut unresolved = vec![];
|
||||
|
|
|
@ -2,14 +2,14 @@ use std::fmt::Write;
|
|||
|
||||
use heck::ToUpperCamelCase;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{ToTokens, format_ident, quote};
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
|
||||
use crate::openapi::parameter::{
|
||||
OpenApiParameter, OpenApiParameterDefault, OpenApiParameterSchema,
|
||||
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)
|
||||
}
|
||||
|
@ -342,13 +351,13 @@ The default value [Self::{}](self::{}#variant.{})"#,
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::openapi::{path::OpenApiPathParameter, schema::OpenApiSchema};
|
||||
use crate::openapi::{path::OpenApiPathParameter, schema::test::get_schema};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn resolve_components() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
let schema = get_schema();
|
||||
|
||||
let mut parameters = 0;
|
||||
let mut unresolved = vec![];
|
||||
|
@ -376,7 +385,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn resolve_inline() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
let schema = get_schema();
|
||||
|
||||
let mut params = 0;
|
||||
let mut unresolved = Vec::new();
|
||||
|
@ -404,7 +413,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn codegen_inline() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(¶m.name);
|
||||
let path = ns.get_ident();
|
||||
(
|
||||
|
@ -352,7 +353,13 @@ impl Path {
|
|||
crate::models::#ty_name
|
||||
}
|
||||
}
|
||||
ParameterType::Array { .. } => param.r#type.codegen_type_name(¶m.name),
|
||||
ParameterType::Array { .. } => {
|
||||
let ty_name = param.r#type.codegen_type_name(¶m.name);
|
||||
|
||||
quote! {
|
||||
crate::request::models::#request_mod_name::#ty_name
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let arg_name = format_ident!("{}", param.value.to_snake_case());
|
||||
|
@ -462,7 +469,12 @@ impl Path {
|
|||
crate::models::#ty_name
|
||||
}
|
||||
}
|
||||
ParameterType::Array { .. } => param.r#type.codegen_type_name(¶m.name),
|
||||
ParameterType::Array { .. } => {
|
||||
let name = param.r#type.codegen_type_name(¶m.name);
|
||||
quote! {
|
||||
crate::request::models::#request_mod_name::#name
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let arg_name = format_ident!("{}", param.value.to_snake_case());
|
||||
|
@ -581,11 +593,11 @@ impl PathNamespace<'_> {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::openapi::schema::OpenApiSchema;
|
||||
use crate::openapi::schema::test::get_schema;
|
||||
|
||||
#[test]
|
||||
fn resolve_paths() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
let schema = get_schema();
|
||||
|
||||
let mut paths = 0;
|
||||
let mut unresolved = vec![];
|
||||
|
@ -613,7 +625,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn codegen_paths() {
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
let schema = get_schema();
|
||||
let resolved = ResolvedSchema::from_open_api(&schema);
|
||||
|
||||
let mut paths = 0;
|
||||
let mut unresolved = vec![];
|
||||
|
@ -625,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -9,14 +11,15 @@ pub enum ParameterLocation {
|
|||
Path,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub enum OpenApiParameterDefault<'a> {
|
||||
Int(i32),
|
||||
Str(&'a str),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OpenApiParameterSchema<'a> {
|
||||
#[serde(rename = "$ref")]
|
||||
pub ref_path: Option<&'a str>,
|
||||
|
@ -27,9 +30,10 @@ 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)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct OpenApiParameter<'a> {
|
||||
pub name: &'a str,
|
||||
pub description: Option<Cow<'a, str>>,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
use super::parameter::OpenApiParameter;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub enum OpenApiPathParameter<'a> {
|
||||
Link {
|
||||
|
@ -14,13 +14,13 @@ pub enum OpenApiPathParameter<'a> {
|
|||
Inline(OpenApiParameter<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct SchemaLink<'a> {
|
||||
#[serde(rename = "$ref")]
|
||||
pub ref_path: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub enum OpenApiResponseBody<'a> {
|
||||
Schema(SchemaLink<'a>),
|
||||
|
@ -30,6 +30,9 @@ pub enum OpenApiResponseBody<'a> {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct OperationId(pub String);
|
||||
|
||||
fn deserialize_response_body<'de, D>(deserializer: D) -> Result<OpenApiResponseBody<'de>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
@ -60,7 +63,8 @@ where
|
|||
Ok(responses.ok.content.json.schema)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct OpenApiPathBody<'a> {
|
||||
pub summary: Option<Cow<'a, str>>,
|
||||
pub description: Option<Cow<'a, str>>,
|
||||
|
@ -72,9 +76,10 @@ pub struct OpenApiPathBody<'a> {
|
|||
deserialize_with = "deserialize_response_body"
|
||||
)]
|
||||
pub response_content: OpenApiResponseBody<'a>,
|
||||
pub operation_id: Option<OperationId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct OpenApiPath<'a> {
|
||||
#[serde(borrow)]
|
||||
pub get: OpenApiPathBody<'a>,
|
||||
|
|
|
@ -3,7 +3,7 @@ use serde::Deserialize;
|
|||
|
||||
use super::{parameter::OpenApiParameter, path::OpenApiPath, r#type::OpenApiType};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct Components<'a> {
|
||||
#[serde(borrow)]
|
||||
pub schemas: IndexMap<&'a str, OpenApiType<'a>>,
|
||||
|
@ -11,7 +11,7 @@ pub struct Components<'a> {
|
|||
pub parameters: IndexMap<&'a str, OpenApiParameter<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct OpenApiSchema<'a> {
|
||||
#[serde(borrow)]
|
||||
pub paths: IndexMap<&'a str, OpenApiPath<'a>>,
|
||||
|
@ -19,20 +19,12 @@ pub struct OpenApiSchema<'a> {
|
|||
pub components: Components<'a>,
|
||||
}
|
||||
|
||||
impl OpenApiSchema<'_> {
|
||||
pub fn read() -> Result<Self, serde_json::Error> {
|
||||
let s = include_str!("../../openapi.json");
|
||||
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn read() {
|
||||
OpenApiSchema::read().unwrap();
|
||||
pub(crate) fn get_schema() -> OpenApiSchema<'static> {
|
||||
let s = include_str!("../../../torn-api/openapi.json");
|
||||
serde_json::from_str(s).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "torn-api"
|
||||
version = "1.2.0"
|
||||
version = "1.5.0"
|
||||
edition = "2021"
|
||||
description = "Auto-generated bindings for the v2 torn api"
|
||||
license-file = { workspace = true }
|
||||
|
@ -13,6 +13,7 @@ scopes = ["builder"]
|
|||
builder = ["requests", "dep:bon"]
|
||||
requests = ["models"]
|
||||
models = ["dep:serde_repr"]
|
||||
strum = ["dep:strum"]
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
@ -31,12 +32,15 @@ futures = { version = "0.3", default-features = false, features = [
|
|||
"std",
|
||||
"async-await",
|
||||
] }
|
||||
chrono = { version = "0.4.41", features = ["serde"] }
|
||||
strum = { version = "0.27.1", features = ["derive"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
torn-api-codegen = { path = "../torn-api-codegen", version = "0.2.3" }
|
||||
torn-api-codegen = { path = "../torn-api-codegen", version = "0.5.0" }
|
||||
syn = { workspace = true, features = ["parsing"] }
|
||||
proc-macro2 = { workspace = true }
|
||||
prettyplease = "0.2"
|
||||
serde_json = { workspace = true }
|
||||
|
|
|
@ -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();
|
||||
|
@ -15,61 +9,23 @@ fn main() {
|
|||
let requests_dest = Path::new(&out_dir).join("requests.rs");
|
||||
let scopes_dest = Path::new(&out_dir).join("scopes.rs");
|
||||
|
||||
let schema = OpenApiSchema::read().unwrap();
|
||||
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();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,12 +4,15 @@ use futures::{Stream, StreamExt};
|
|||
use http::{header::AUTHORIZATION, HeaderMap, HeaderValue};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::request::{ApiRequest, ApiResponse, IntoRequest};
|
||||
#[cfg(feature = "scopes")]
|
||||
use crate::scopes::{
|
||||
BulkFactionScope, BulkForumScope, BulkMarketScope, BulkRacingScope, BulkTornScope,
|
||||
BulkUserScope, FactionScope, ForumScope, MarketScope, RacingScope, TornScope, UserScope,
|
||||
};
|
||||
use crate::{
|
||||
request::{ApiRequest, ApiResponse, IntoRequest},
|
||||
scopes::{BulkKeyScope, KeyScope},
|
||||
};
|
||||
|
||||
pub trait Executor: Sized {
|
||||
type Error: From<serde_json::Error> + From<crate::ApiError> + Send;
|
||||
|
@ -125,6 +128,8 @@ pub trait ExecutorExt: Executor + Sized {
|
|||
fn racing(self) -> RacingScope<Self>;
|
||||
|
||||
fn forum(self) -> ForumScope<Self>;
|
||||
|
||||
fn key(self) -> KeyScope<Self>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "scopes")]
|
||||
|
@ -155,6 +160,10 @@ where
|
|||
fn forum(self) -> ForumScope<Self> {
|
||||
ForumScope::new(self)
|
||||
}
|
||||
|
||||
fn key(self) -> KeyScope<Self> {
|
||||
KeyScope::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "scopes")]
|
||||
|
@ -170,6 +179,8 @@ pub trait BulkExecutorExt: BulkExecutor + Sized {
|
|||
fn racing_bulk(self) -> BulkRacingScope<Self>;
|
||||
|
||||
fn forum_bulk(self) -> BulkForumScope<Self>;
|
||||
|
||||
fn key_bulk(self) -> BulkKeyScope<Self>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "scopes")]
|
||||
|
@ -200,6 +211,10 @@ where
|
|||
fn forum_bulk(self) -> BulkForumScope<Self> {
|
||||
BulkForumScope::new(self)
|
||||
}
|
||||
|
||||
fn key_bulk(self) -> BulkKeyScope<Self> {
|
||||
BulkKeyScope::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReqwestClient(reqwest::Client);
|
||||
|
|
|
@ -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();
|
||||
|
@ -366,6 +369,15 @@ pub(super) mod test {
|
|||
faction_scope.lookup(|b| b).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn faction_reports() {
|
||||
let client = test_client().await;
|
||||
|
||||
let faction_scope = FactionScope(&client);
|
||||
|
||||
faction_scope.reports(|b| b).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn forum_categories() {
|
||||
let client = test_client().await;
|
||||
|
@ -415,7 +427,7 @@ pub(super) mod test {
|
|||
let forum_scope = ForumScope(&client);
|
||||
|
||||
forum_scope
|
||||
.threads_for_category_ids("2".to_owned(), |b| b)
|
||||
.threads_for_category_ids([2].into(), |b| b)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -486,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() {
|
||||
|
@ -639,10 +651,7 @@ pub(super) mod test {
|
|||
|
||||
let torn_scope = TornScope(&client);
|
||||
|
||||
torn_scope
|
||||
.items_for_ids("1".to_owned(), |b| b)
|
||||
.await
|
||||
.unwrap();
|
||||
torn_scope.items_for_ids([1].into(), |b| b).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -954,4 +963,25 @@ pub(super) mod test {
|
|||
|
||||
client.user().attacks(|b| b).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_reports() {
|
||||
let client = test_client().await;
|
||||
|
||||
client.user().reports(|b| b).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn key_info() {
|
||||
let client = test_client().await;
|
||||
|
||||
client.key().info(|b| b).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn key_log() {
|
||||
let client = test_client().await;
|
||||
|
||||
client.key().log(|b| b).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "torn-key-pool"
|
||||
version = "1.1.1"
|
||||
version = "1.1.3"
|
||||
edition = "2021"
|
||||
authors = ["Pyrit [2111649]"]
|
||||
license-file = { workspace = true }
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#[cfg(feature = "postgres")]
|
||||
pub mod postgres;
|
||||
|
||||
use std::{collections::HashMap, future::Future, sync::Arc, time::Duration};
|
||||
use std::{collections::HashMap, future::Future, ops::Deref, sync::Arc, time::Duration};
|
||||
|
||||
use futures::{future::BoxFuture, FutureExt, Stream, StreamExt};
|
||||
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
|
||||
|
@ -354,7 +354,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
struct KeyPoolInner<S>
|
||||
pub struct KeyPoolInner<S>
|
||||
where
|
||||
S: KeyPoolStorage,
|
||||
{
|
||||
|
@ -473,6 +473,16 @@ where
|
|||
inner: Arc<KeyPoolInner<S>>,
|
||||
}
|
||||
|
||||
impl<S> Deref for KeyPool<S>
|
||||
where
|
||||
S: KeyPoolStorage,
|
||||
{
|
||||
type Target = KeyPoolInner<S>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
enum RequestResult {
|
||||
Response(ApiResponse),
|
||||
Retry,
|
||||
|
|
Loading…
Reference in a new issue