From 907f898cada0b3be3b0ddcaffb3ec2473471d88c Mon Sep 17 00:00:00 2001 From: C0D3 M4513R <28912031+C0D3-M4513R@users.noreply.github.com> Date: Sun, 9 Aug 2026 10:23:54 +0200 Subject: [PATCH 1/3] Revert all template changes Signed-off-by: C0D3 M4513R <28912031+C0D3-M4513R@users.noreply.github.com> --- templates/Cargo.mustache | 8 ++--- templates/api_doc.mustache | 4 ++- templates/lib.mustache | 1 - templates/model.mustache | 6 ++++ templates/partial_header.mustache | 13 ++++++++ templates/reqwest-trait/api.mustache | 12 +++---- templates/reqwest-trait/api_mod.mustache | 40 +--------------------- templates/reqwest/api.mustache | 42 ++++++++++++++++-------- templates/reqwest/api_mod.mustache | 40 +--------------------- templates/reqwest/configuration.mustache | 2 +- 10 files changed, 63 insertions(+), 105 deletions(-) diff --git a/templates/Cargo.mustache b/templates/Cargo.mustache index 553781c0..16b981be 100644 --- a/templates/Cargo.mustache +++ b/templates/Cargo.mustache @@ -66,7 +66,7 @@ secrecy = "0.8.0" {{/withAWSV4Signature}} {{#reqwest}} {{^supportAsync}} -reqwest = { version = "^0.13", default-features = false, features = ["json", "blocking", "multipart", "cookies", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "blocking", "multipart", "query", "form"] } {{#supportMiddleware}} reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query", "form"] } {{/supportMiddleware}} @@ -75,10 +75,10 @@ reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query {{#useAsyncFileStream}} tokio = { version = "^1.46.0", features = ["fs"] } tokio-util = { version = "^0.7", features = ["codec"] } -reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "cookies", "stream", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "stream", "query", "form"] } {{/useAsyncFileStream}} {{^useAsyncFileStream}} -reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "cookies", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "query", "form"] } {{/useAsyncFileStream}} {{#supportMiddleware}} reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query", "form"] } @@ -97,7 +97,7 @@ rustls = ["reqwest/rustls"] {{/reqwest}} {{#reqwestTrait}} async-trait = "^0.1" -reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "cookies", "stream", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "stream", "query", "form"] } {{#supportMiddleware}} reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query", "form"] } {{/supportMiddleware}} diff --git a/templates/api_doc.mustache b/templates/api_doc.mustache index 0088937f..c3608175 100644 --- a/templates/api_doc.mustache +++ b/templates/api_doc.mustache @@ -1,4 +1,6 @@ -# {{{invokerPackage}}}\{{{classname}}} +# {{{invokerPackage}}}\{{{classname}}}{{#description}} + +{{{.}}}{{/description}} All URIs are relative to *{{{basePath}}}* diff --git a/templates/lib.mustache b/templates/lib.mustache index 13943afc..b51df2b1 100644 --- a/templates/lib.mustache +++ b/templates/lib.mustache @@ -1,5 +1,4 @@ #![allow(unused_imports)] -#![allow(non_camel_case_types)] #![allow(clippy::too_many_arguments)] extern crate serde_repr; diff --git a/templates/model.mustache b/templates/model.mustache index ab1630ec..42e9f2ef 100644 --- a/templates/model.mustache +++ b/templates/model.mustache @@ -175,10 +175,16 @@ impl {{{classname}}} { {{/oneOf.isEmpty}} {{^oneOf.isEmpty}} {{! TODO: add other vars that are not part of the oneOf}} +{{#description}} +/// {{{.}}} +{{/description}} #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum {{classname}} { {{#composedSchemas.oneOf}} + {{#description}} + /// {{{.}}} + {{/description}} {{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}), {{/composedSchemas.oneOf}} } diff --git a/templates/partial_header.mustache b/templates/partial_header.mustache index e69de29b..408d841d 100644 --- a/templates/partial_header.mustache +++ b/templates/partial_header.mustache @@ -0,0 +1,13 @@ +/* + {{#appName}} + * {{{.}}} + * + {{/appName}} + {{#appDescription}} + * {{{.}}} + * + {{/appDescription}} + * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} + * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} + * Generated by: https://openapi-generator.tech + */ diff --git a/templates/reqwest-trait/api.mustache b/templates/reqwest-trait/api.mustache index 6de964d8..cf16c0fc 100644 --- a/templates/reqwest-trait/api.mustache +++ b/templates/reqwest-trait/api.mustache @@ -90,6 +90,9 @@ impl {{classname}}Client { {{/useBonBuilder}} pub struct {{{operationIdCamelCase}}}Params { {{/-first}} + {{#description}} + /// {{{.}}} + {{/description}} pub {{{paramName}}}: {{! ### Option Start }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! @@ -116,6 +119,9 @@ pub struct {{{operationIdCamelCase}}}Params { impl {{classname}} for {{classname}}Client { {{#operations}} {{#operation}} + {{#description}} + /// {{{.}}} + {{/description}} {{#notes}} /// {{{.}}} {{/notes}} @@ -462,14 +468,8 @@ impl {{classname}} for {{classname}}Client { {{/isMultipart}} {{#hasBodyParam}} {{#bodyParams}} - {{^required}} - if let Some({{{paramName}}}) = {{{paramName}}} { - {{/required}} local_var_req_builder = local_var_req_builder.json(&{{{paramName}}}); {{/bodyParams}} - {{^required}} - } - {{/required}} {{/hasBodyParam}} let local_var_req = local_var_req_builder.build()?; diff --git a/templates/reqwest-trait/api_mod.mustache b/templates/reqwest-trait/api_mod.mustache index e6618b22..41a4ba0f 100644 --- a/templates/reqwest-trait/api_mod.mustache +++ b/templates/reqwest-trait/api_mod.mustache @@ -95,46 +95,8 @@ impl From for Error { } } -fn hex_digit(b: u8) -> Option { - match b { - b'0'..=b'9' => Some(b - b'0'), - b'a'..=b'f' => Some(b - b'a' + 10), - b'A'..=b'F' => Some(b - b'A' + 10), - _ => None, - } -} - -fn parse_percent_encoded(bytes: &[u8; 3]) -> Option { - if bytes[0] != b'%' { - return None; - } - let hi = hex_digit(bytes[1])?; - let lo = hex_digit(bytes[2])?; - Some((hi << 4) | lo) -} - pub fn urlencode>(s: T) -> String { - ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()) - .map(|string| { - debug_assert!( - !string.starts_with('%') || string.len() == 3, - "the iterator should yield percent-encoded strings of exactly 3 bytes, or unescaped strings" - ); - - let parsed = match string.as_bytes().try_into() { - Ok(bytes) => parse_percent_encoded(bytes), - Err(_) => None, - }; - - // The VRChat API deviates from the application/x-www-form-urlencoded percent-encode set, for values like the InstanceID. - // The characters bellow should remain unchanged for URI parameters, or requests will be rejected as malformed. - match parsed { - Some(b'(') => "(", - Some(b')') => ")", - _ => string - } - }) - .collect() + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() } pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { diff --git a/templates/reqwest/api.mustache b/templates/reqwest/api.mustache index 3b545d5c..5224b0c4 100644 --- a/templates/reqwest/api.mustache +++ b/templates/reqwest/api.mustache @@ -22,6 +22,9 @@ use tokio_util::codec::{BytesCodec, FramedRead}; #[derive(Clone, Debug)] pub struct {{{operationIdCamelCase}}}Params { {{/-first}} + {{#description}} + /// {{{.}}} + {{/description}} pub {{{paramName}}}: {{! ### Option Start }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! @@ -90,6 +93,9 @@ pub enum {{{operationIdCamelCase}}}Error { {{#operations}} {{#operation}} +{{#description}} +/// {{{.}}} +{{/description}} {{#notes}} /// {{{.}}} {{/notes}} @@ -444,22 +450,38 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: if let Some(ref param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { {{#isArray}} for value in param_value { - multipart_form = multipart_form.part("{{{baseName}}}", value.get_multipart().await?); + let file = TokioFile::open(value).await?; + let stream = FramedRead::new(file, BytesCodec::new()); + let file_name = value.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); + let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); + multipart_form = multipart_form.part("{{{baseName}}}", file_part); } {{/isArray}} {{^isArray}} - multipart_form = multipart_form.part("{{{baseName}}}", param_value.get_multipart().await?); + let file = TokioFile::open(param_value).await?; + let stream = FramedRead::new(file, BytesCodec::new()); + let file_name = param_value.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); + let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); + multipart_form = multipart_form.part("{{{baseName}}}", file_part); {{/isArray}} } {{/required}} {{#required}} {{#isArray}} - for value in {{{vendorExtensions.x-rust-param-identifier}}} { - multipart_form = multipart_form.part("{{{baseName}}}", value.get_multipart().await?); + for value in &{{{vendorExtensions.x-rust-param-identifier}}} { + let file = TokioFile::open(value).await?; + let stream = FramedRead::new(file, BytesCodec::new()); + let file_name = value.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); + let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); + multipart_form = multipart_form.part("{{{baseName}}}", file_part); } {{/isArray}} {{^isArray}} - multipart_form = multipart_form.part("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}}.get_multipart().await?); + let file = TokioFile::open(&{{{vendorExtensions.x-rust-param-identifier}}}).await?; + let stream = FramedRead::new(file, BytesCodec::new()); + let file_name = {{{vendorExtensions.x-rust-param-identifier}}}.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); + let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); + multipart_form = multipart_form.part("{{{baseName}}}", file_part); {{/isArray}} {{/required}} {{/supportAsync}} @@ -467,15 +489,7 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: {{^isFile}} {{#required}} {{^isNullable}} - macro_rules! serialize { - ($form:ident, data, $data:ident) => { - $form = $form.text("data", serde_json::to_string(&$data)?); - }; - ($form:ident, $tag:ident, $data:ident) => { - $form = $form.text(stringify!($tag), $data{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); - }; - } - serialize!(multipart_form, {{{baseName}}}, {{{vendorExtensions.x-rust-param-identifier}}}); + multipart_form = multipart_form.text("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); {{/isNullable}} {{#isNullable}} match {{{vendorExtensions.x-rust-param-identifier}}} { diff --git a/templates/reqwest/api_mod.mustache b/templates/reqwest/api_mod.mustache index f12de67d..ada95b7c 100644 --- a/templates/reqwest/api_mod.mustache +++ b/templates/reqwest/api_mod.mustache @@ -118,46 +118,8 @@ impl From for Error { } } -fn hex_digit(b: u8) -> Option { - match b { - b'0'..=b'9' => Some(b - b'0'), - b'a'..=b'f' => Some(b - b'a' + 10), - b'A'..=b'F' => Some(b - b'A' + 10), - _ => None, - } -} - -fn parse_percent_encoded(bytes: &[u8; 3]) -> Option { - if bytes[0] != b'%' { - return None; - } - let hi = hex_digit(bytes[1])?; - let lo = hex_digit(bytes[2])?; - Some((hi << 4) | lo) -} - pub fn urlencode>(s: T) -> String { - ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()) - .map(|string| { - debug_assert!( - !string.starts_with('%') || string.len() == 3, - "the iterator should yield percent-encoded strings of exactly 3 bytes, or unescaped strings" - ); - - let parsed = match string.as_bytes().try_into() { - Ok(bytes) => parse_percent_encoded(bytes), - Err(_) => None, - }; - - // The VRChat API deviates from the application/x-www-form-urlencoded percent-encode set, for values like the InstanceID. - // The characters bellow should remain unchanged for URI parameters, or requests will be rejected as malformed. - match parsed { - Some(b'(') => "(", - Some(b')') => ")", - _ => string - } - }) - .collect() + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() } pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { diff --git a/templates/reqwest/configuration.mustache b/templates/reqwest/configuration.mustache index afb40abc..e29c73b1 100644 --- a/templates/reqwest/configuration.mustache +++ b/templates/reqwest/configuration.mustache @@ -93,7 +93,7 @@ impl Default for Configuration { Configuration { base_path: "{{{basePath}}}".to_owned(), user_agent: {{#httpUserAgent}}Some("{{{.}}}".to_owned()){{/httpUserAgent}}{{^httpUserAgent}}Some("OpenAPI-Generator/{{{version}}}/rust".to_owned()){{/httpUserAgent}}, - client: {{#supportMiddleware}}reqwest_middleware::ClientBuilder::new(reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::builder().cookie_store(true).build().unwrap()).build(){{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new(){{/supportMiddleware}}, + client: {{#supportMiddleware}}reqwest_middleware::ClientBuilder::new(reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new()).build(){{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new(){{/supportMiddleware}}, {{^supportTokenSource}} basic_auth: None, oauth_access_token: None, From 2a6bac9be27c215568d8c0bcca44bb42962672e9 Mon Sep 17 00:00:00 2001 From: C0D3 M4513R <28912031+C0D3-M4513R@users.noreply.github.com> Date: Sun, 9 Aug 2026 10:24:27 +0200 Subject: [PATCH 2/3] Update to 7.24.0 Signed-off-by: C0D3 M4513R <28912031+C0D3-M4513R@users.noreply.github.com> --- openapitools.json | 2 +- templates/Cargo.mustache | 3 +++ templates/model.mustache | 30 +++++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/openapitools.json b/openapitools.json index 077c2dbc..f29b197f 100644 --- a/openapitools.json +++ b/openapitools.json @@ -2,6 +2,6 @@ "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", "spaces": 2, "generator-cli": { - "version": "7.21.0" + "version": "7.24.0" } } diff --git a/templates/Cargo.mustache b/templates/Cargo.mustache index 16b981be..d1cb78a1 100644 --- a/templates/Cargo.mustache +++ b/templates/Cargo.mustache @@ -45,6 +45,9 @@ url = "^2.5" {{#hasUUIDs}} uuid = { version = "^1.8", features = ["serde", "v4"] } {{/hasUUIDs}} +{{#usesChronoTypes}} +chrono = { version = "^0.4", features = ["serde"] } +{{/usesChronoTypes}} {{#hyper}} {{#hyper0x}} hyper = { version = "~0.14", features = ["full"] } diff --git a/templates/model.mustache b/templates/model.mustache index 42e9f2ef..a2c60b11 100644 --- a/templates/model.mustache +++ b/templates/model.mustache @@ -5,7 +5,9 @@ use serde::{Deserialize, Serialize}; {{#model}} {{^isEnum}}{{#vendorExtensions.x-rust-has-byte-array}} use serde_with::serde_as; -{{/vendorExtensions.x-rust-has-byte-array}}{{/isEnum}} +{{/vendorExtensions.x-rust-has-byte-array}}{{#vendorExtensions.x-rust-has-integer-property-enum}} +use serde_repr::{Serialize_repr,Deserialize_repr}; +{{/vendorExtensions.x-rust-has-integer-property-enum}}{{/isEnum}} {{#isEnum}} {{#isInteger}} use serde_repr::{Serialize_repr,Deserialize_repr}; @@ -200,6 +202,31 @@ impl Default for {{classname}} { {{!-- for properties that are of enum type --}} {{#vars}} {{#isEnum}} +{{#isInteger}} +/// {{{description}}} +#[repr(i64)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize_repr, Deserialize_repr)] +pub enum {{{enumName}}} { +{{#allowableValues}} +{{#enumVars}} + {{{name}}} = {{{value}}}, +{{/enumVars}} +{{/allowableValues}} +} + +impl std::fmt::Display for {{{enumName}}} { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + {{#allowableValues}} + {{#enumVars}} + Self::{{{name}}} => "{{{value}}}", + {{/enumVars}} + {{/allowableValues}} + }) + } +} +{{/isInteger}} +{{^isInteger}} /// {{{description}}} #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum {{{enumName}}} { @@ -210,6 +237,7 @@ pub enum {{{enumName}}} { {{/enumVars}} {{/allowableValues}} } +{{/isInteger}} impl Default for {{{enumName}}} { fn default() -> {{{enumName}}} { From 555b414fb20a3f1e3c55d8cb6fbb370d5d46306b Mon Sep 17 00:00:00 2001 From: C0D3 M4513R <28912031+C0D3-M4513R@users.noreply.github.com> Date: Sun, 9 Aug 2026 10:24:34 +0200 Subject: [PATCH 3/3] Revert "Revert all template changes" This reverts commit 907f898cada0b3be3b0ddcaffb3ec2473471d88c. --- templates/Cargo.mustache | 8 ++--- templates/api_doc.mustache | 4 +-- templates/lib.mustache | 1 + templates/model.mustache | 6 ---- templates/partial_header.mustache | 13 -------- templates/reqwest-trait/api.mustache | 12 +++---- templates/reqwest-trait/api_mod.mustache | 40 +++++++++++++++++++++- templates/reqwest/api.mustache | 42 ++++++++---------------- templates/reqwest/api_mod.mustache | 40 +++++++++++++++++++++- templates/reqwest/configuration.mustache | 2 +- 10 files changed, 105 insertions(+), 63 deletions(-) diff --git a/templates/Cargo.mustache b/templates/Cargo.mustache index d1cb78a1..68a6c2ce 100644 --- a/templates/Cargo.mustache +++ b/templates/Cargo.mustache @@ -69,7 +69,7 @@ secrecy = "0.8.0" {{/withAWSV4Signature}} {{#reqwest}} {{^supportAsync}} -reqwest = { version = "^0.13", default-features = false, features = ["json", "blocking", "multipart", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "blocking", "multipart", "cookies", "query", "form"] } {{#supportMiddleware}} reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query", "form"] } {{/supportMiddleware}} @@ -78,10 +78,10 @@ reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query {{#useAsyncFileStream}} tokio = { version = "^1.46.0", features = ["fs"] } tokio-util = { version = "^0.7", features = ["codec"] } -reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "stream", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "cookies", "stream", "query", "form"] } {{/useAsyncFileStream}} {{^useAsyncFileStream}} -reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "cookies", "query", "form"] } {{/useAsyncFileStream}} {{#supportMiddleware}} reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query", "form"] } @@ -100,7 +100,7 @@ rustls = ["reqwest/rustls"] {{/reqwest}} {{#reqwestTrait}} async-trait = "^0.1" -reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "stream", "query", "form"] } +reqwest = { version = "^0.13", default-features = false, features = ["json", "multipart", "cookies", "stream", "query", "form"] } {{#supportMiddleware}} reqwest-middleware = { version = "^0.5", features = ["json", "multipart", "query", "form"] } {{/supportMiddleware}} diff --git a/templates/api_doc.mustache b/templates/api_doc.mustache index c3608175..0088937f 100644 --- a/templates/api_doc.mustache +++ b/templates/api_doc.mustache @@ -1,6 +1,4 @@ -# {{{invokerPackage}}}\{{{classname}}}{{#description}} - -{{{.}}}{{/description}} +# {{{invokerPackage}}}\{{{classname}}} All URIs are relative to *{{{basePath}}}* diff --git a/templates/lib.mustache b/templates/lib.mustache index b51df2b1..13943afc 100644 --- a/templates/lib.mustache +++ b/templates/lib.mustache @@ -1,4 +1,5 @@ #![allow(unused_imports)] +#![allow(non_camel_case_types)] #![allow(clippy::too_many_arguments)] extern crate serde_repr; diff --git a/templates/model.mustache b/templates/model.mustache index a2c60b11..4c6c4106 100644 --- a/templates/model.mustache +++ b/templates/model.mustache @@ -177,16 +177,10 @@ impl {{{classname}}} { {{/oneOf.isEmpty}} {{^oneOf.isEmpty}} {{! TODO: add other vars that are not part of the oneOf}} -{{#description}} -/// {{{.}}} -{{/description}} #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum {{classname}} { {{#composedSchemas.oneOf}} - {{#description}} - /// {{{.}}} - {{/description}} {{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}), {{/composedSchemas.oneOf}} } diff --git a/templates/partial_header.mustache b/templates/partial_header.mustache index 408d841d..e69de29b 100644 --- a/templates/partial_header.mustache +++ b/templates/partial_header.mustache @@ -1,13 +0,0 @@ -/* - {{#appName}} - * {{{.}}} - * - {{/appName}} - {{#appDescription}} - * {{{.}}} - * - {{/appDescription}} - * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} - * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} - * Generated by: https://openapi-generator.tech - */ diff --git a/templates/reqwest-trait/api.mustache b/templates/reqwest-trait/api.mustache index cf16c0fc..6de964d8 100644 --- a/templates/reqwest-trait/api.mustache +++ b/templates/reqwest-trait/api.mustache @@ -90,9 +90,6 @@ impl {{classname}}Client { {{/useBonBuilder}} pub struct {{{operationIdCamelCase}}}Params { {{/-first}} - {{#description}} - /// {{{.}}} - {{/description}} pub {{{paramName}}}: {{! ### Option Start }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! @@ -119,9 +116,6 @@ pub struct {{{operationIdCamelCase}}}Params { impl {{classname}} for {{classname}}Client { {{#operations}} {{#operation}} - {{#description}} - /// {{{.}}} - {{/description}} {{#notes}} /// {{{.}}} {{/notes}} @@ -468,8 +462,14 @@ impl {{classname}} for {{classname}}Client { {{/isMultipart}} {{#hasBodyParam}} {{#bodyParams}} + {{^required}} + if let Some({{{paramName}}}) = {{{paramName}}} { + {{/required}} local_var_req_builder = local_var_req_builder.json(&{{{paramName}}}); {{/bodyParams}} + {{^required}} + } + {{/required}} {{/hasBodyParam}} let local_var_req = local_var_req_builder.build()?; diff --git a/templates/reqwest-trait/api_mod.mustache b/templates/reqwest-trait/api_mod.mustache index 41a4ba0f..e6618b22 100644 --- a/templates/reqwest-trait/api_mod.mustache +++ b/templates/reqwest-trait/api_mod.mustache @@ -95,8 +95,46 @@ impl From for Error { } } +fn hex_digit(b: u8) -> Option { + match b { + b'0'..=b'9' => Some(b - b'0'), + b'a'..=b'f' => Some(b - b'a' + 10), + b'A'..=b'F' => Some(b - b'A' + 10), + _ => None, + } +} + +fn parse_percent_encoded(bytes: &[u8; 3]) -> Option { + if bytes[0] != b'%' { + return None; + } + let hi = hex_digit(bytes[1])?; + let lo = hex_digit(bytes[2])?; + Some((hi << 4) | lo) +} + pub fn urlencode>(s: T) -> String { - ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()) + .map(|string| { + debug_assert!( + !string.starts_with('%') || string.len() == 3, + "the iterator should yield percent-encoded strings of exactly 3 bytes, or unescaped strings" + ); + + let parsed = match string.as_bytes().try_into() { + Ok(bytes) => parse_percent_encoded(bytes), + Err(_) => None, + }; + + // The VRChat API deviates from the application/x-www-form-urlencoded percent-encode set, for values like the InstanceID. + // The characters bellow should remain unchanged for URI parameters, or requests will be rejected as malformed. + match parsed { + Some(b'(') => "(", + Some(b')') => ")", + _ => string + } + }) + .collect() } pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { diff --git a/templates/reqwest/api.mustache b/templates/reqwest/api.mustache index 5224b0c4..3b545d5c 100644 --- a/templates/reqwest/api.mustache +++ b/templates/reqwest/api.mustache @@ -22,9 +22,6 @@ use tokio_util::codec::{BytesCodec, FramedRead}; #[derive(Clone, Debug)] pub struct {{{operationIdCamelCase}}}Params { {{/-first}} - {{#description}} - /// {{{.}}} - {{/description}} pub {{{paramName}}}: {{! ### Option Start }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! @@ -93,9 +90,6 @@ pub enum {{{operationIdCamelCase}}}Error { {{#operations}} {{#operation}} -{{#description}} -/// {{{.}}} -{{/description}} {{#notes}} /// {{{.}}} {{/notes}} @@ -450,38 +444,22 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: if let Some(ref param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { {{#isArray}} for value in param_value { - let file = TokioFile::open(value).await?; - let stream = FramedRead::new(file, BytesCodec::new()); - let file_name = value.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); - let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); - multipart_form = multipart_form.part("{{{baseName}}}", file_part); + multipart_form = multipart_form.part("{{{baseName}}}", value.get_multipart().await?); } {{/isArray}} {{^isArray}} - let file = TokioFile::open(param_value).await?; - let stream = FramedRead::new(file, BytesCodec::new()); - let file_name = param_value.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); - let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); - multipart_form = multipart_form.part("{{{baseName}}}", file_part); + multipart_form = multipart_form.part("{{{baseName}}}", param_value.get_multipart().await?); {{/isArray}} } {{/required}} {{#required}} {{#isArray}} - for value in &{{{vendorExtensions.x-rust-param-identifier}}} { - let file = TokioFile::open(value).await?; - let stream = FramedRead::new(file, BytesCodec::new()); - let file_name = value.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); - let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); - multipart_form = multipart_form.part("{{{baseName}}}", file_part); + for value in {{{vendorExtensions.x-rust-param-identifier}}} { + multipart_form = multipart_form.part("{{{baseName}}}", value.get_multipart().await?); } {{/isArray}} {{^isArray}} - let file = TokioFile::open(&{{{vendorExtensions.x-rust-param-identifier}}}).await?; - let stream = FramedRead::new(file, BytesCodec::new()); - let file_name = {{{vendorExtensions.x-rust-param-identifier}}}.file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_default(); - let file_part = reqwest::multipart::Part::stream(reqwest::Body::wrap_stream(stream)).file_name(file_name); - multipart_form = multipart_form.part("{{{baseName}}}", file_part); + multipart_form = multipart_form.part("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}}.get_multipart().await?); {{/isArray}} {{/required}} {{/supportAsync}} @@ -489,7 +467,15 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: {{^isFile}} {{#required}} {{^isNullable}} - multipart_form = multipart_form.text("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + macro_rules! serialize { + ($form:ident, data, $data:ident) => { + $form = $form.text("data", serde_json::to_string(&$data)?); + }; + ($form:ident, $tag:ident, $data:ident) => { + $form = $form.text(stringify!($tag), $data{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + }; + } + serialize!(multipart_form, {{{baseName}}}, {{{vendorExtensions.x-rust-param-identifier}}}); {{/isNullable}} {{#isNullable}} match {{{vendorExtensions.x-rust-param-identifier}}} { diff --git a/templates/reqwest/api_mod.mustache b/templates/reqwest/api_mod.mustache index ada95b7c..f12de67d 100644 --- a/templates/reqwest/api_mod.mustache +++ b/templates/reqwest/api_mod.mustache @@ -118,8 +118,46 @@ impl From for Error { } } +fn hex_digit(b: u8) -> Option { + match b { + b'0'..=b'9' => Some(b - b'0'), + b'a'..=b'f' => Some(b - b'a' + 10), + b'A'..=b'F' => Some(b - b'A' + 10), + _ => None, + } +} + +fn parse_percent_encoded(bytes: &[u8; 3]) -> Option { + if bytes[0] != b'%' { + return None; + } + let hi = hex_digit(bytes[1])?; + let lo = hex_digit(bytes[2])?; + Some((hi << 4) | lo) +} + pub fn urlencode>(s: T) -> String { - ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()) + .map(|string| { + debug_assert!( + !string.starts_with('%') || string.len() == 3, + "the iterator should yield percent-encoded strings of exactly 3 bytes, or unescaped strings" + ); + + let parsed = match string.as_bytes().try_into() { + Ok(bytes) => parse_percent_encoded(bytes), + Err(_) => None, + }; + + // The VRChat API deviates from the application/x-www-form-urlencoded percent-encode set, for values like the InstanceID. + // The characters bellow should remain unchanged for URI parameters, or requests will be rejected as malformed. + match parsed { + Some(b'(') => "(", + Some(b')') => ")", + _ => string + } + }) + .collect() } pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { diff --git a/templates/reqwest/configuration.mustache b/templates/reqwest/configuration.mustache index e29c73b1..afb40abc 100644 --- a/templates/reqwest/configuration.mustache +++ b/templates/reqwest/configuration.mustache @@ -93,7 +93,7 @@ impl Default for Configuration { Configuration { base_path: "{{{basePath}}}".to_owned(), user_agent: {{#httpUserAgent}}Some("{{{.}}}".to_owned()){{/httpUserAgent}}{{^httpUserAgent}}Some("OpenAPI-Generator/{{{version}}}/rust".to_owned()){{/httpUserAgent}}, - client: {{#supportMiddleware}}reqwest_middleware::ClientBuilder::new(reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new()).build(){{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new(){{/supportMiddleware}}, + client: {{#supportMiddleware}}reqwest_middleware::ClientBuilder::new(reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::builder().cookie_store(true).build().unwrap()).build(){{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new(){{/supportMiddleware}}, {{^supportTokenSource}} basic_auth: None, oauth_access_token: None,