fix(rust): simplify new function optional logic (#17865)

* fix(rust): simplify None logic

Used to be a code path (where isFreeFormObject and something else) where we could end up with two `: None` `: None`'s

This does add the None for the !required && !isArray && !isMap && !isNullable case (where nothing would have been printed before.

Above for the type on the individual vars inside the struct definition the logic is simply if not required then it's optional.
If it's optional we want a None when creating a new struct.

* ci: add tests for failure
This commit is contained in:
Nathan Shaaban
2024-02-19 14:26:29 +00:00
committed by GitHub
parent eb7d9b1605
commit 76d743b63b
39 changed files with 1179 additions and 1 deletions

View File

@@ -0,0 +1,24 @@
use std::rc::Rc;
use hyper;
use super::configuration::Configuration;
pub struct APIClient {
default_api: Box<dyn crate::apis::DefaultApi>,
}
impl APIClient {
pub fn new<C: hyper::client::connect::Connect>(configuration: Configuration<C>) -> APIClient
where C: Clone + std::marker::Send + Sync + 'static {
let rc = Rc::new(configuration);
APIClient {
default_api: Box::new(crate::apis::DefaultApiClient::new(rc.clone())),
}
}
pub fn default_api(&self) -> &dyn crate::apis::DefaultApi{
self.default_api.as_ref()
}
}

View File

@@ -0,0 +1,43 @@
/*
* EmptyObject
*
* Ensure rust supports empty objects using serde::Value
*
* The version of the OpenAPI document: 1.0.0
*
* Generated by: https://openapi-generator.tech
*/
use hyper;
pub struct Configuration<C: hyper::client::connect::Connect>
where C: Clone + std::marker::Send + Sync + 'static {
pub base_path: String,
pub user_agent: Option<String>,
pub client: hyper::client::Client<C>,
pub basic_auth: Option<BasicAuth>,
pub oauth_access_token: Option<String>,
pub api_key: Option<ApiKey>,
// TODO: take an oauth2 token source, similar to the go one
}
pub type BasicAuth = (String, Option<String>);
pub struct ApiKey {
pub prefix: Option<String>,
pub key: String,
}
impl<C: hyper::client::connect::Connect> Configuration<C>
where C: Clone + std::marker::Send + Sync {
pub fn new(client: hyper::client::Client<C>) -> Configuration<C> {
Configuration {
base_path: "http://localhost".to_owned(),
user_agent: Some("OpenAPI-Generator/1.0.0/rust".to_owned()),
client,
basic_auth: None,
oauth_access_token: None,
api_key: None,
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* EmptyObject
*
* Ensure rust supports empty objects using serde::Value
*
* The version of the OpenAPI document: 1.0.0
*
* Generated by: https://openapi-generator.tech
*/
use std::rc::Rc;
use std::borrow::Borrow;
use std::pin::Pin;
#[allow(unused_imports)]
use std::option::Option;
use hyper;
use futures::Future;
use super::{Error, configuration};
use super::request as __internal_request;
pub struct DefaultApiClient<C: hyper::client::connect::Connect>
where C: Clone + std::marker::Send + Sync + 'static {
configuration: Rc<configuration::Configuration<C>>,
}
impl<C: hyper::client::connect::Connect> DefaultApiClient<C>
where C: Clone + std::marker::Send + Sync {
pub fn new(configuration: Rc<configuration::Configuration<C>>) -> DefaultApiClient<C> {
DefaultApiClient {
configuration,
}
}
}
pub trait DefaultApi {
fn endpoint_get(&self, ) -> Pin<Box<dyn Future<Output = Result<crate::models::EmptyObject, Error>>>>;
}
impl<C: hyper::client::connect::Connect>DefaultApi for DefaultApiClient<C>
where C: Clone + std::marker::Send + Sync {
#[allow(unused_mut)]
fn endpoint_get(&self, ) -> Pin<Box<dyn Future<Output = Result<crate::models::EmptyObject, Error>>>> {
let mut req = __internal_request::Request::new(hyper::Method::GET, "/endpoint".to_string())
;
req.execute(self.configuration.borrow())
}
}

View File

@@ -0,0 +1,54 @@
use http;
use hyper;
use serde_json;
#[derive(Debug)]
pub enum Error {
Api(ApiError),
Header(hyper::http::header::InvalidHeaderValue),
Http(http::Error),
Hyper(hyper::Error),
Serde(serde_json::Error),
UriError(http::uri::InvalidUri),
}
#[derive(Debug)]
pub struct ApiError {
pub code: hyper::StatusCode,
pub body: hyper::body::Body,
}
impl From<(hyper::StatusCode, hyper::body::Body)> for Error {
fn from(e: (hyper::StatusCode, hyper::body::Body)) -> Self {
Error::Api(ApiError {
code: e.0,
body: e.1,
})
}
}
impl From<http::Error> for Error {
fn from(e: http::Error) -> Self {
return Error::Http(e)
}
}
impl From<hyper::Error> for Error {
fn from(e: hyper::Error) -> Self {
return Error::Hyper(e)
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
return Error::Serde(e)
}
}
mod request;
mod default_api;
pub use self::default_api::{ DefaultApi, DefaultApiClient };
pub mod configuration;
pub mod client;

View File

@@ -0,0 +1,242 @@
use std::collections::HashMap;
use std::pin::Pin;
use futures;
use futures::Future;
use futures::future::*;
use hyper;
use hyper::header::{AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, HeaderValue, USER_AGENT};
use serde;
use serde_json;
use super::{configuration, Error};
pub(crate) struct ApiKey {
pub in_header: bool,
pub in_query: bool,
pub param_name: String,
}
impl ApiKey {
fn key(&self, prefix: &Option<String>, key: &str) -> String {
match prefix {
None => key.to_owned(),
Some(ref prefix) => format!("{} {}", prefix, key),
}
}
}
#[allow(dead_code)]
pub(crate) enum Auth {
None,
ApiKey(ApiKey),
Basic,
Oauth,
}
/// If the authorization type is unspecified then it will be automatically detected based
/// on the configuration. This functionality is useful when the OpenAPI definition does not
/// include an authorization scheme.
pub(crate) struct Request {
auth: Option<Auth>,
method: hyper::Method,
path: String,
query_params: HashMap<String, String>,
no_return_type: bool,
path_params: HashMap<String, String>,
form_params: HashMap<String, String>,
header_params: HashMap<String, String>,
// TODO: multiple body params are possible technically, but not supported here.
serialized_body: Option<String>,
}
#[allow(dead_code)]
impl Request {
pub fn new(method: hyper::Method, path: String) -> Self {
Request {
auth: None,
method,
path,
query_params: HashMap::new(),
path_params: HashMap::new(),
form_params: HashMap::new(),
header_params: HashMap::new(),
serialized_body: None,
no_return_type: false,
}
}
pub fn with_body_param<T: serde::Serialize>(mut self, param: T) -> Self {
self.serialized_body = Some(serde_json::to_string(&param).unwrap());
self
}
pub fn with_header_param(mut self, basename: String, param: String) -> Self {
self.header_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_query_param(mut self, basename: String, param: String) -> Self {
self.query_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_path_param(mut self, basename: String, param: String) -> Self {
self.path_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_form_param(mut self, basename: String, param: String) -> Self {
self.form_params.insert(basename, param);
self
}
pub fn returns_nothing(mut self) -> Self {
self.no_return_type = true;
self
}
pub fn with_auth(mut self, auth: Auth) -> Self {
self.auth = Some(auth);
self
}
pub fn execute<'a, C, U>(
self,
conf: &configuration::Configuration<C>,
) -> Pin<Box<dyn Future<Output=Result<U, Error>> + 'a>>
where
C: hyper::client::connect::Connect + Clone + std::marker::Send + Sync,
U: Sized + std::marker::Send + 'a,
for<'de> U: serde::Deserialize<'de>,
{
let mut query_string = ::url::form_urlencoded::Serializer::new("".to_owned());
let mut path = self.path;
for (k, v) in self.path_params {
// replace {id} with the value of the id path param
path = path.replace(&format!("{{{}}}", k), &v);
}
for (key, val) in self.query_params {
query_string.append_pair(&key, &val);
}
let mut uri_str = format!("{}{}", conf.base_path, path);
let query_string_str = query_string.finish();
if !query_string_str.is_empty() {
uri_str += "?";
uri_str += &query_string_str;
}
let uri: hyper::Uri = match uri_str.parse() {
Err(e) => return Box::pin(futures::future::err(Error::UriError(e))),
Ok(u) => u,
};
let mut req_builder = hyper::Request::builder()
.uri(uri)
.method(self.method);
// Detect the authorization type if it hasn't been set.
let auth = self.auth.unwrap_or_else(||
if conf.api_key.is_some() {
panic!("Cannot automatically set the API key from the configuration, it must be specified in the OpenAPI definition")
} else if conf.oauth_access_token.is_some() {
Auth::Oauth
} else if conf.basic_auth.is_some() {
Auth::Basic
} else {
Auth::None
}
);
match auth {
Auth::ApiKey(apikey) => {
if let Some(ref key) = conf.api_key {
let val = apikey.key(&key.prefix, &key.key);
if apikey.in_query {
query_string.append_pair(&apikey.param_name, &val);
}
if apikey.in_header {
req_builder = req_builder.header(&apikey.param_name, val);
}
}
}
Auth::Basic => {
if let Some(ref auth_conf) = conf.basic_auth {
let mut text = auth_conf.0.clone();
text.push(':');
if let Some(ref pass) = auth_conf.1 {
text.push_str(&pass[..]);
}
let encoded = base64::encode(&text);
req_builder = req_builder.header(AUTHORIZATION, encoded);
}
}
Auth::Oauth => {
if let Some(ref token) = conf.oauth_access_token {
let text = "Bearer ".to_owned() + token;
req_builder = req_builder.header(AUTHORIZATION, text);
}
}
Auth::None => {}
}
if let Some(ref user_agent) = conf.user_agent {
req_builder = req_builder.header(USER_AGENT, match HeaderValue::from_str(user_agent) {
Ok(header_value) => header_value,
Err(e) => return Box::pin(futures::future::err(super::Error::Header(e)))
});
}
for (k, v) in self.header_params {
req_builder = req_builder.header(&k, v);
}
let req_headers = req_builder.headers_mut().unwrap();
let request_result = if self.form_params.len() > 0 {
req_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/x-www-form-urlencoded"));
let mut enc = ::url::form_urlencoded::Serializer::new("".to_owned());
for (k, v) in self.form_params {
enc.append_pair(&k, &v);
}
req_builder.body(hyper::Body::from(enc.finish()))
} else if let Some(body) = self.serialized_body {
req_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
req_headers.insert(CONTENT_LENGTH, body.len().into());
req_builder.body(hyper::Body::from(body))
} else {
req_builder.body(hyper::Body::default())
};
let request = match request_result {
Ok(request) => request,
Err(e) => return Box::pin(futures::future::err(Error::from(e)))
};
let no_return_type = self.no_return_type;
Box::pin(conf.client
.request(request)
.map_err(|e| Error::from(e))
.and_then(move |response| {
let status = response.status();
if !status.is_success() {
futures::future::err::<U, Error>(Error::from((status, response.into_body()))).boxed()
} else if no_return_type {
// This is a hack; if there's no_ret_type, U is (), but serde_json gives an
// error when deserializing "" into (), so deserialize 'null' into it
// instead.
// An alternate option would be to require U: Default, and then return
// U::default() here instead since () implements that, but then we'd
// need to impl default for all models.
futures::future::ok::<U, Error>(serde_json::from_str("null").expect("serde null value")).boxed()
} else {
hyper::body::to_bytes(response.into_body())
.map(|bytes| serde_json::from_slice(&bytes.unwrap()))
.map_err(|e| Error::from(e)).boxed()
}
}))
}
}

View File

@@ -0,0 +1,11 @@
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate url;
extern crate hyper;
extern crate futures;
pub mod apis;
pub mod models;

View File

@@ -0,0 +1,28 @@
/*
* EmptyObject
*
* Ensure rust supports empty objects using serde::Value
*
* The version of the OpenAPI document: 1.0.0
*
* Generated by: https://openapi-generator.tech
*/
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct EmptyObject {
#[serde(rename = "emptyObject", skip_serializing_if = "Option::is_none")]
pub empty_object: Option<serde_json::Value>,
}
impl EmptyObject {
pub fn new() -> EmptyObject {
EmptyObject {
empty_object: None,
}
}
}

View File

@@ -0,0 +1,2 @@
pub mod empty_object;
pub use self::empty_object::EmptyObject;