diff --git a/Cargo.lock b/Cargo.lock index a0fb607..a1d972c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1817,7 +1817,7 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "typify" version = "0.0.10-dev" -source = "git+https://github.com/oxidecomputer/typify#ab2d3e18f624ce4a55278c0846ebb5f936134023" +source = "git+https://github.com/oxidecomputer/typify#19e441b4972270d85ea4d73b41c66264f5d6eb82" dependencies = [ "typify-impl", "typify-macro", @@ -1826,7 +1826,7 @@ dependencies = [ [[package]] name = "typify-impl" version = "0.0.10-dev" -source = "git+https://github.com/oxidecomputer/typify#ab2d3e18f624ce4a55278c0846ebb5f936134023" +source = "git+https://github.com/oxidecomputer/typify#19e441b4972270d85ea4d73b41c66264f5d6eb82" dependencies = [ "heck", "log", @@ -1844,7 +1844,7 @@ dependencies = [ [[package]] name = "typify-macro" version = "0.0.10-dev" -source = "git+https://github.com/oxidecomputer/typify#ab2d3e18f624ce4a55278c0846ebb5f936134023" +source = "git+https://github.com/oxidecomputer/typify#19e441b4972270d85ea4d73b41c66264f5d6eb82" dependencies = [ "proc-macro2", "quote", diff --git a/progenitor-impl/tests/output/test_default_params_builder.out b/progenitor-impl/tests/output/test_default_params_builder.out new file mode 100644 index 0000000..cc576a9 --- /dev/null +++ b/progenitor-impl/tests/output/test_default_params_builder.out @@ -0,0 +1,287 @@ +#[allow(unused_imports)] +use progenitor_client::{encode_path, RequestBuilderExt}; +pub use progenitor_client::{ByteStream, Error, ResponseValue}; +pub mod types { + use serde::{Deserialize, Serialize}; + #[allow(unused_imports)] + use std::convert::TryFrom; + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct BodyWithDefaults { + #[serde(rename = "forty-two", default = "defaults::default_u64::")] + pub forty_two: u32, + pub s: String, + #[serde(default = "defaults::body_with_defaults_something")] + pub something: Option, + #[serde(default)] + pub yes: bool, + } + + impl BodyWithDefaults { + pub fn builder() -> builder::BodyWithDefaults { + builder::BodyWithDefaults::default() + } + } + + ///Error information from a response. + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Error { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_code: Option, + pub message: String, + pub request_id: String, + } + + impl Error { + pub fn builder() -> builder::Error { + builder::Error::default() + } + } + + mod builder { + pub struct BodyWithDefaults { + forty_two: Result, + s: Result, + something: Result, String>, + yes: Result, + } + + impl Default for BodyWithDefaults { + fn default() -> Self { + Self { + forty_two: Ok(super::defaults::default_u64::()), + s: Err("no value supplied for s".to_string()), + something: Ok(super::defaults::body_with_defaults_something()), + yes: Ok(Default::default()), + } + } + } + + impl BodyWithDefaults { + pub fn forty_two(mut self, value: T) -> Self + where + T: std::convert::TryInto, + T::Error: std::fmt::Display, + { + self.forty_two = value + .try_into() + .map_err(|e| format!("error converting supplied value for forty_two: {}", e)); + self + } + pub fn s(mut self, value: T) -> Self + where + T: std::convert::TryInto, + T::Error: std::fmt::Display, + { + self.s = value + .try_into() + .map_err(|e| format!("error converting supplied value for s: {}", e)); + self + } + pub fn something(mut self, value: T) -> Self + where + T: std::convert::TryInto>, + T::Error: std::fmt::Display, + { + self.something = value + .try_into() + .map_err(|e| format!("error converting supplied value for something: {}", e)); + self + } + pub fn yes(mut self, value: T) -> Self + where + T: std::convert::TryInto, + T::Error: std::fmt::Display, + { + self.yes = value + .try_into() + .map_err(|e| format!("error converting supplied value for yes: {}", e)); + self + } + } + + impl std::convert::TryFrom for super::BodyWithDefaults { + type Error = String; + fn try_from(value: BodyWithDefaults) -> Result { + Ok(Self { + forty_two: value.forty_two?, + s: value.s?, + something: value.something?, + yes: value.yes?, + }) + } + } + + pub struct Error { + error_code: Result, String>, + message: Result, + request_id: Result, + } + + impl Default for Error { + fn default() -> Self { + Self { + error_code: Ok(Default::default()), + message: Err("no value supplied for message".to_string()), + request_id: Err("no value supplied for request_id".to_string()), + } + } + } + + impl Error { + pub fn error_code(mut self, value: T) -> Self + where + T: std::convert::TryInto>, + T::Error: std::fmt::Display, + { + self.error_code = value + .try_into() + .map_err(|e| format!("error converting supplied value for error_code: {}", e)); + self + } + pub fn message(mut self, value: T) -> Self + where + T: std::convert::TryInto, + T::Error: std::fmt::Display, + { + self.message = value + .try_into() + .map_err(|e| format!("error converting supplied value for message: {}", e)); + self + } + pub fn request_id(mut self, value: T) -> Self + where + T: std::convert::TryInto, + T::Error: std::fmt::Display, + { + self.request_id = value + .try_into() + .map_err(|e| format!("error converting supplied value for request_id: {}", e)); + self + } + } + + impl std::convert::TryFrom for super::Error { + type Error = String; + fn try_from(value: Error) -> Result { + Ok(Self { + error_code: value.error_code?, + message: value.message?, + request_id: value.request_id?, + }) + } + } + } + + mod defaults { + pub(super) fn default_u64() -> T + where + T: std::convert::TryFrom, + >::Error: std::fmt::Debug, + { + T::try_from(V).unwrap() + } + + pub(super) fn body_with_defaults_something() -> Option { + Some(true) + } + } +} + +#[derive(Clone, Debug)] +pub struct Client { + pub(crate) baseurl: String, + pub(crate) client: reqwest::Client, +} + +impl Client { + pub fn new(baseurl: &str) -> Self { + let dur = std::time::Duration::from_secs(15); + let client = reqwest::ClientBuilder::new() + .connect_timeout(dur) + .timeout(dur) + .build() + .unwrap(); + Self::new_with_client(baseurl, client) + } + + pub fn new_with_client(baseurl: &str, client: reqwest::Client) -> Self { + Self { + baseurl: baseurl.to_string(), + client, + } + } + + pub fn baseurl(&self) -> &String { + &self.baseurl + } + + pub fn client(&self) -> &reqwest::Client { + &self.client + } +} + +impl Client { + ///Sends a `POST` request to `/` + ///```ignore + /// let response = client.default_params() + /// .body(body) + /// .send() + /// .await; + /// ``` + pub fn default_params(&self) -> builder::DefaultParams { + builder::DefaultParams::new(self) + } +} + +pub mod builder { + use super::types; + #[allow(unused_imports)] + use super::{encode_path, ByteStream, Error, RequestBuilderExt, ResponseValue}; + #[allow(unused_imports)] + use std::convert::TryInto; + ///Builder for [`Client::default_params`] + /// + ///[`Client::default_params`]: super::Client::default_params + #[derive(Debug, Clone)] + pub struct DefaultParams<'a> { + client: &'a super::Client, + body: Result, + } + + impl<'a> DefaultParams<'a> { + pub fn new(client: &'a super::Client) -> Self { + Self { + client, + body: Err("body was not initialized".to_string()), + } + } + + pub fn body(mut self, value: V) -> Self + where + V: TryInto, + { + self.body = value + .try_into() + .map_err(|_| "conversion to `BodyWithDefaults` for body failed".to_string()); + self + } + + ///Sends a `POST` request to `/` + pub async fn send(self) -> Result, Error> { + let Self { client, body } = self; + let body = body.map_err(Error::InvalidRequest)?; + let url = format!("{}/", client.baseurl,); + let request = client.client.post(url).json(&body).build()?; + let result = client.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200..=299 => Ok(ResponseValue::stream(response)), + _ => Err(Error::ErrorResponse(ResponseValue::stream(response))), + } + } + } +} + +pub mod prelude { + pub use self::super::Client; +} diff --git a/progenitor-impl/tests/output/test_default_params.out b/progenitor-impl/tests/output/test_default_params_positional.out similarity index 92% rename from progenitor-impl/tests/output/test_default_params.out rename to progenitor-impl/tests/output/test_default_params_positional.out index b269aa9..9eadbca 100644 --- a/progenitor-impl/tests/output/test_default_params.out +++ b/progenitor-impl/tests/output/test_default_params_positional.out @@ -10,6 +10,8 @@ pub mod types { #[serde(rename = "forty-two", default = "defaults::default_u64::")] pub forty_two: u32, pub s: String, + #[serde(default = "defaults::body_with_defaults_something")] + pub something: Option, #[serde(default)] pub yes: bool, } @@ -31,6 +33,10 @@ pub mod types { { T::try_from(V).unwrap() } + + pub(super) fn body_with_defaults_something() -> Option { + Some(true) + } } } diff --git a/progenitor-impl/tests/test_specific.rs b/progenitor-impl/tests/test_specific.rs index e6e0a41..3ca7667 100644 --- a/progenitor-impl/tests/test_specific.rs +++ b/progenitor-impl/tests/test_specific.rs @@ -9,7 +9,7 @@ use dropshot::{ use http::Response; use hyper::Body; use openapiv3::OpenAPI; -use progenitor_impl::Generator; +use progenitor_impl::{GenerationSettings, Generator, InterfaceStyle}; use schemars::JsonSchema; use serde::Deserialize; @@ -113,12 +113,18 @@ struct BodyWithDefaults { yes: bool, #[serde(default = "forty_two", rename = "forty-two")] forty_two: u32, + #[serde(default = "yes_yes")] + something: Option, } fn forty_two() -> u32 { 42 } +fn yes_yes() -> Option { + Some(true) +} + #[endpoint { method = POST, path = "/", @@ -148,7 +154,16 @@ fn test_default_params() { let mut generator = Generator::default(); let output = generator.generate_text_normalize_comments(&spec).unwrap(); expectorate::assert_contents( - format!("tests/output/{}.out", "test_default_params"), + format!("tests/output/{}.out", "test_default_params_positional"), &output, - ) + ); + + let mut generator = Generator::new( + GenerationSettings::default().with_interface(InterfaceStyle::Builder), + ); + let output = generator.generate_text_normalize_comments(&spec).unwrap(); + expectorate::assert_contents( + format!("tests/output/{}.out", "test_default_params_builder"), + &output, + ); }