diff --git a/cargo-progenitor/src/main.rs b/cargo-progenitor/src/main.rs index 191675a7..123dbedf 100644 --- a/cargo-progenitor/src/main.rs +++ b/cargo-progenitor/src/main.rs @@ -65,8 +65,8 @@ enum InterfaceArg { impl From for InterfaceStyle { fn from(arg: InterfaceArg) -> Self { match arg { - InterfaceArg::Positional => InterfaceStyle::Positional, - InterfaceArg::Builder => InterfaceStyle::Builder, + InterfaceArg::Positional => Self::Positional, + InterfaceArg::Builder => Self::Builder, } } } @@ -80,8 +80,8 @@ enum TagArg { impl From for TagStyle { fn from(arg: TagArg) -> Self { match arg { - TagArg::Merged => TagStyle::Merged, - TagArg::Separate => TagStyle::Separate, + TagArg::Merged => Self::Merged, + TagArg::Separate => Self::Separate, } } } @@ -92,7 +92,7 @@ fn reformat_code(input: String) -> String { wrap_comments: Some(true), ..Default::default() }; - space_out_items(rustfmt_wrapper::rustfmt_config(config, input).unwrap()).unwrap() + space_out_items(&rustfmt_wrapper::rustfmt_config(config, input).unwrap()).unwrap() } fn save

(p: P, data: &str) -> Result<()> @@ -131,7 +131,7 @@ fn main() -> Result<()> { println!("-----------------------------------------------------"); for (idx, type_entry) in type_space.iter_types().enumerate() { let n = type_entry.describe(); - println!("{:>4} {}", idx, n); + println!("{idx:>4} {n}"); } println!("-----------------------------------------------------"); println!(); @@ -156,7 +156,7 @@ fn main() -> Result<()> { name, version, &args.license_name, ); if let Some(registry_name) = args.registry_name { - tomlout.extend(format!("publish = [\"{}\"]\n", registry_name).chars()); + tomlout.extend(format!("publish = [\"{registry_name}\"]\n").chars()); } tomlout.extend( format!( @@ -178,7 +178,7 @@ fn main() -> Result<()> { // Create the Rust source file containing the generated client: let lib_code = if args.include_client { - format!("mod progenitor_client;\n\n{}", api_code) + format!("mod progenitor_client;\n\n{api_code}") } else { api_code.to_string() }; @@ -198,7 +198,7 @@ fn main() -> Result<()> { } Err(e) => { - println!("gen fail: {:?}", e); + println!("gen fail: {e:?}"); bail!("generation experienced errors"); } } @@ -265,7 +265,7 @@ pub fn dependencies(builder: Generator, include_client: bool) -> Vec { } else { "*" }; - let client_version_dep = format!("progenitor-client = \"{}\"", crate_version); + let client_version_dep = format!("progenitor-client = \"{crate_version}\""); deps.push(client_version_dep); } diff --git a/cargo-progenitor/tests/test_cmd.rs b/cargo-progenitor/tests/test_cmd.rs index 101bbae0..5789ff02 100644 --- a/cargo-progenitor/tests/test_cmd.rs +++ b/cargo-progenitor/tests/test_cmd.rs @@ -1,11 +1,10 @@ // Copyright 2025 Oxide Computer Company -use assert_cmd::Command; +use assert_cmd::cargo; #[test] fn test_help() { - Command::cargo_bin("cargo-progenitor") - .unwrap() + cargo::cargo_bin_cmd!("cargo-progenitor") .arg("progenitor") .arg("--help") .assert() diff --git a/example-build/build.rs b/example-build/build.rs index 2119be59..7fd75973 100644 --- a/example-build/build.rs +++ b/example-build/build.rs @@ -8,7 +8,7 @@ use std::{ fn main() { let src = "../sample_openapi/keeper.json"; - println!("cargo:rerun-if-changed={}", src); + println!("cargo:rerun-if-changed={src}"); let file = File::open(src).unwrap(); let spec = serde_json::from_reader(file).unwrap(); let mut generator = progenitor::Generator::default(); diff --git a/example-build/src/main.rs b/example-build/src/main.rs index c0fa53a8..dbfe659d 100644 --- a/example-build/src/main.rs +++ b/example-build/src/main.rs @@ -8,8 +8,8 @@ fn main() { let _ = client.enrol( "auth-token", &types::EnrolBody { - host: "".to_string(), - key: "".to_string(), + host: String::new(), + key: String::new(), }, ); } diff --git a/example-macro/src/main.rs b/example-macro/src/main.rs index 5ace903b..b093c5fd 100644 --- a/example-macro/src/main.rs +++ b/example-macro/src/main.rs @@ -5,7 +5,7 @@ use progenitor::generate_api; generate_api!( spec = "../sample_openapi/keeper.json", pre_hook = (|request| { - println!("doing this {:?}", request); + println!("doing this {request:?}"); }), pre_hook_async = crate::add_auth_headers, post_hook = crate::all_done, diff --git a/example-wasm/build.rs b/example-wasm/build.rs index 2119be59..7fd75973 100644 --- a/example-wasm/build.rs +++ b/example-wasm/build.rs @@ -8,7 +8,7 @@ use std::{ fn main() { let src = "../sample_openapi/keeper.json"; - println!("cargo:rerun-if-changed={}", src); + println!("cargo:rerun-if-changed={src}"); let file = File::open(src).unwrap(); let spec = serde_json::from_reader(file).unwrap(); let mut generator = progenitor::Generator::default(); diff --git a/example-wasm/src/main.rs b/example-wasm/src/main.rs index c0fa53a8..dbfe659d 100644 --- a/example-wasm/src/main.rs +++ b/example-wasm/src/main.rs @@ -8,8 +8,8 @@ fn main() { let _ = client.enrol( "auth-token", &types::EnrolBody { - host: "".to_string(), - key: "".to_string(), + host: String::new(), + key: String::new(), }, ); } diff --git a/progenitor-client/src/progenitor_client.rs b/progenitor-client/src/progenitor_client.rs index 6f8dcca0..7b70fa1f 100644 --- a/progenitor-client/src/progenitor_client.rs +++ b/progenitor-client/src/progenitor_client.rs @@ -21,7 +21,7 @@ type InnerByteStream = std::pin::Pin Self { @@ -355,14 +355,14 @@ impl Error { /// Returns the status code, if the error was generated from a response. pub fn status(&self) -> Option { match self { - Error::InvalidRequest(_) => None, - Error::Custom(_) => None, - Error::CommunicationError(e) => e.status(), - Error::ErrorResponse(rv) => Some(rv.status()), - Error::InvalidUpgrade(e) => e.status(), - Error::ResponseBodyError(e) => e.status(), - Error::InvalidResponsePayload(_, _) => None, - Error::UnexpectedResponse(r) => Some(r.status()), + Self::InvalidRequest(_) => None, + Self::Custom(_) => None, + Self::CommunicationError(e) => e.status(), + Self::ErrorResponse(rv) => Some(rv.status()), + Self::InvalidUpgrade(e) => e.status(), + Self::ResponseBodyError(e) => e.status(), + Self::InvalidResponsePayload(_, _) => None, + Self::UnexpectedResponse(r) => Some(r.status()), } } @@ -372,10 +372,10 @@ impl Error { /// various error response bodies. pub fn into_untyped(self) -> Error { match self { - Error::InvalidRequest(s) => Error::InvalidRequest(s), - Error::Custom(s) => Error::Custom(s), - Error::CommunicationError(e) => Error::CommunicationError(e), - Error::ErrorResponse(ResponseValue { + Self::InvalidRequest(s) => Error::InvalidRequest(s), + Self::Custom(s) => Error::Custom(s), + Self::CommunicationError(e) => Error::CommunicationError(e), + Self::ErrorResponse(ResponseValue { inner: _, status, headers, @@ -384,10 +384,10 @@ impl Error { status, headers, }), - Error::InvalidUpgrade(e) => Error::InvalidUpgrade(e), - Error::ResponseBodyError(e) => Error::ResponseBodyError(e), - Error::InvalidResponsePayload(b, e) => Error::InvalidResponsePayload(b, e), - Error::UnexpectedResponse(r) => Error::UnexpectedResponse(r), + Self::InvalidUpgrade(e) => Error::InvalidUpgrade(e), + Self::ResponseBodyError(e) => Error::ResponseBodyError(e), + Self::InvalidResponsePayload(b, e) => Error::InvalidResponsePayload(b, e), + Self::UnexpectedResponse(r) => Error::UnexpectedResponse(r), } } } @@ -416,30 +416,30 @@ where { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Error::InvalidRequest(s) => { - write!(f, "Invalid Request: {}", s)?; + Self::InvalidRequest(s) => { + write!(f, "Invalid Request: {s}")?; } - Error::CommunicationError(e) => { - write!(f, "Communication Error: {}", e)?; + Self::CommunicationError(e) => { + write!(f, "Communication Error: {e}")?; } - Error::ErrorResponse(rve) => { + Self::ErrorResponse(rve) => { write!(f, "Error Response: ")?; rve.fmt_info(f)?; } - Error::InvalidUpgrade(e) => { - write!(f, "Invalid Response Upgrade: {}", e)?; + Self::InvalidUpgrade(e) => { + write!(f, "Invalid Response Upgrade: {e}")?; } - Error::ResponseBodyError(e) => { - write!(f, "Invalid Response Body Bytes: {}", e)?; + Self::ResponseBodyError(e) => { + write!(f, "Invalid Response Body Bytes: {e}")?; } - Error::InvalidResponsePayload(b, e) => { - write!(f, "Invalid Response Payload ({:?}): {}", b, e)?; + Self::InvalidResponsePayload(b, e) => { + write!(f, "Invalid Response Payload ({b:?}): {e}")?; } - Error::UnexpectedResponse(r) => { - write!(f, "Unexpected Response: {:?}", r)?; + Self::UnexpectedResponse(r) => { + write!(f, "Unexpected Response: {r:?}")?; } - Error::Custom(s) => { - write!(f, "Error: {}", s)?; + Self::Custom(s) => { + write!(f, "Error: {s}")?; } } @@ -497,10 +497,10 @@ where { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - Error::CommunicationError(e) => Some(e), - Error::InvalidUpgrade(e) => Some(e), - Error::ResponseBodyError(e) => Some(e), - Error::InvalidResponsePayload(_b, e) => Some(e), + Self::CommunicationError(e) => Some(e), + Self::InvalidUpgrade(e) => Some(e), + Self::ResponseBodyError(e) => Some(e), + Self::InvalidResponsePayload(_b, e) => Some(e), _ => None, } } diff --git a/progenitor-impl/src/cli.rs b/progenitor-impl/src/cli.rs index e1bdf30c..2b7ff453 100644 --- a/progenitor-impl/src/cli.rs +++ b/progenitor-impl/src/cli.rs @@ -213,121 +213,115 @@ impl Generator { let (_, error_kind) = self.extract_responses(method, OperationResponseStatus::is_error_or_default); - let execute_and_output = match method.dropshot_paginated { - // Normal, one-shot API calls. - None => { - let success_output = match success_kind { - crate::method::OperationResponseKind::Type(_) => { - quote! { - { - self.config.success_item(&r); - Ok(()) - } + let execute_and_output = if method.dropshot_paginated.is_some() { + let success_type = match success_kind { + crate::method::OperationResponseKind::Type(type_id) => { + self.type_space.get_type(&type_id).unwrap().ident() + } + crate::method::OperationResponseKind::None => quote! { () }, + crate::method::OperationResponseKind::Raw => todo!(), + crate::method::OperationResponseKind::Upgrade => todo!(), + }; + let error_output = match error_kind { + crate::method::OperationResponseKind::Type(_) + | crate::method::OperationResponseKind::None => { + quote! { + { + self.config.list_end_error(&r); + return Err(anyhow::Error::new(r)) } } - crate::method::OperationResponseKind::None => { - quote! { - { - self.config.success_no_item(&r); - Ok(()) - } + } + crate::method::OperationResponseKind::Raw + | crate::method::OperationResponseKind::Upgrade => { + quote! { + { + todo!() } } - crate::method::OperationResponseKind::Raw - | crate::method::OperationResponseKind::Upgrade => { - quote! { - { - todo!() - } + } + }; + quote! { + self.config.list_start::<#success_type>(); + + // We're using "limit" as both the maximum page size and + // as the full limit. It's not ideal in that we could + // reduce the limit with each iteration and we might get a + // bunch of results we don't display... but it's fine. + let mut stream = futures::StreamExt::take( + request.stream(), + matches + .get_one::("limit") + .map_or(usize::MAX, |x| x.get() as usize)); + + loop { + match futures::TryStreamExt::try_next(&mut stream).await { + Err(r) => #error_output + Ok(None) => { + self.config.list_end_success::<#success_type>(); + return Ok(()); } - } - }; - - let error_output = match error_kind { - crate::method::OperationResponseKind::Type(_) - | crate::method::OperationResponseKind::None => { - quote! { - { - self.config.error(&r); - Err(anyhow::Error::new(r)) - } + Ok(Some(value)) => { + self.config.list_item(&value); } } - crate::method::OperationResponseKind::Raw - | crate::method::OperationResponseKind::Upgrade => { - quote! { - { - todo!() - } + } + } + } else { + let success_output = match success_kind { + crate::method::OperationResponseKind::Type(_) => { + quote! { + { + self.config.success_item(&r); + Ok(()) } } - }; - - quote! { - let result = request.send().await; - - match result { - Ok(r) => #success_output - Err(r) => #error_output - } } - } - - // Paginated APIs for which we iterate over each item. - Some(_) => { - let success_type = match success_kind { - crate::method::OperationResponseKind::Type(type_id) => { - self.type_space.get_type(&type_id).unwrap().ident() + crate::method::OperationResponseKind::None => { + quote! { + { + self.config.success_no_item(&r); + Ok(()) + } } - crate::method::OperationResponseKind::None => quote! { () }, - crate::method::OperationResponseKind::Raw => todo!(), - crate::method::OperationResponseKind::Upgrade => todo!(), - }; - let error_output = match error_kind { - crate::method::OperationResponseKind::Type(_) - | crate::method::OperationResponseKind::None => { - quote! { - { - self.config.list_end_error(&r); - return Err(anyhow::Error::new(r)) - } + } + crate::method::OperationResponseKind::Raw + | crate::method::OperationResponseKind::Upgrade => { + quote! { + { + todo!() } } - crate::method::OperationResponseKind::Raw - | crate::method::OperationResponseKind::Upgrade => { - quote! { - { - todo!() - } + } + }; + + let error_output = match error_kind { + crate::method::OperationResponseKind::Type(_) + | crate::method::OperationResponseKind::None => { + quote! { + { + self.config.error(&r); + Err(anyhow::Error::new(r)) } } - }; - quote! { - self.config.list_start::<#success_type>(); - - // We're using "limit" as both the maximum page size and - // as the full limit. It's not ideal in that we could - // reduce the limit with each iteration and we might get a - // bunch of results we don't display... but it's fine. - let mut stream = futures::StreamExt::take( - request.stream(), - matches - .get_one::("limit") - .map_or(usize::MAX, |x| x.get() as usize)); - - loop { - match futures::TryStreamExt::try_next(&mut stream).await { - Err(r) => #error_output - Ok(None) => { - self.config.list_end_success::<#success_type>(); - return Ok(()); - } - Ok(Some(value)) => { - self.config.list_item(&value); - } + } + crate::method::OperationResponseKind::Raw + | crate::method::OperationResponseKind::Upgrade => { + quote! { + { + todo!() } } } + }; + + quote! { + let result = request.send().await; + + match result { + Ok(r) => #success_output + Err(r) => #error_output + } } }; @@ -380,8 +374,8 @@ impl Generator { OperationParameterKind::Body(_) => continue, OperationParameterKind::Path => true, - OperationParameterKind::Query(required) => *required, - OperationParameterKind::Header(required) => *required, + OperationParameterKind::Query(required) + | OperationParameterKind::Header(required) => *required, }; // For paginated endpoints, we don't generate 'page_token' args. @@ -389,8 +383,8 @@ impl Generator { continue; } - let first_page_required = first_page_required_set - .map_or(false, |required| required.contains(¶m.api_name)); + let first_page_required = + first_page_required_set.is_some_and(|required| required.contains(¶m.api_name)); let volitionality = if innately_required || first_page_required { Volitionality::Required @@ -428,7 +422,7 @@ impl Generator { } }; - args.add_arg(arg_name, CliArg { parser, consumer }) + args.add_arg(arg_name, CliArg { parser, consumer }); } let maybe_body_type_id = method @@ -452,14 +446,14 @@ impl Generator { match details { typify::TypeDetails::Struct(struct_info) => { for prop_info in struct_info.properties_info() { - self.cli_method_body_arg(&mut args, prop_info) + self.cli_method_body_arg(&mut args, prop_info); } } _ => { // If the body is not a struct, we don't know what's // required or how to generate it - args.body_required() + args.body_required(); } } } @@ -595,9 +589,9 @@ impl Generator { }) } }; - args.add_arg(prop_name, CliArg { parser, consumer }) + args.add_arg(prop_name, CliArg { parser, consumer }); } else if required { - args.body_required() + args.body_required(); } // Cases @@ -613,6 +607,7 @@ impl Generator { } } +#[derive(Clone, Copy)] enum Volitionality { Optional, Required, diff --git a/progenitor-impl/src/httpmock.rs b/progenitor-impl/src/httpmock.rs index 1e7f9745..aaa1d611 100644 --- a/progenitor-impl/src/httpmock.rs +++ b/progenitor-impl/src/httpmock.rs @@ -74,7 +74,7 @@ impl Generator { let crate_path = syn::TypePath { qself: None, path: syn::parse_str(crate_path) - .unwrap_or_else(|_| panic!("{} is not a valid identifier", crate_path)), + .unwrap_or_else(|_| panic!("{crate_path} is not a valid identifier")), }; let code = quote! { @@ -345,7 +345,8 @@ impl Generator { }, ) } - crate::method::OperationResponseKind::None => Default::default(), + crate::method::OperationResponseKind::None + | crate::method::OperationResponseKind::Upgrade => Default::default(), crate::method::OperationResponseKind::Raw => ( quote! { value: ::serde_json::Value, @@ -355,7 +356,6 @@ impl Generator { .json_body(value) }, ), - crate::method::OperationResponseKind::Upgrade => Default::default(), }; match status_code { diff --git a/progenitor-impl/src/lib.rs b/progenitor-impl/src/lib.rs index 68454ece..a2762634 100644 --- a/progenitor-impl/src/lib.rs +++ b/progenitor-impl/src/lib.rs @@ -85,35 +85,25 @@ struct CrateSpec { } /// Style of generated client. -#[derive(Clone, Deserialize, PartialEq, Eq)] +#[derive(Clone, Deserialize, PartialEq, Eq, Default)] pub enum InterfaceStyle { /// Use positional style. + #[default] Positional, /// Use builder style. Builder, } -impl Default for InterfaceStyle { - fn default() -> Self { - Self::Positional - } -} - /// Style for using the OpenAPI tags when generating names in the client. -#[derive(Clone, Deserialize)] +#[derive(Clone, Deserialize, Default)] pub enum TagStyle { /// Merge tags to create names in the generated client. + #[default] Merged, /// Use each tag name to create separate names in the generated client. Separate, } -impl Default for TagStyle { - fn default() -> Self { - Self::Merged - } -} - impl GenerationSettings { /// Create new generator settings with default values. pub fn new() -> Self { @@ -296,13 +286,13 @@ impl Generator { .replace .iter() .for_each(|(type_name, (replace_name, impls))| { - type_settings.with_replacement(type_name, replace_name, impls.iter().cloned()); + type_settings.with_replacement(type_name, replace_name, impls.iter().copied()); }); settings .convert .iter() .for_each(|(schema, type_name, impls)| { - type_settings.with_conversion(schema.clone(), type_name, impls.iter().cloned()); + type_settings.with_conversion(schema.clone(), type_name, impls.iter().copied()); }); // Set the map type if specified. @@ -667,13 +657,13 @@ impl Generator { } /// Add newlines after end-braces at <= two levels of indentation. -pub fn space_out_items(content: String) -> Result { +pub fn space_out_items(content: &str) -> Result { Ok(if cfg!(not(windows)) { - let regex = regex::Regex::new(r#"(\n\s*})(\n\s{0,8}[^} ])"#).unwrap(); - regex.replace_all(&content, "$1\n$2").to_string() + let regex = regex::Regex::new(r"(\n\s*})(\n\s{0,8}[^} ])").unwrap(); + regex.replace_all(content, "$1\n$2").to_string() } else { - let regex = regex::Regex::new(r#"(\n\s*})(\r\n\s{0,8}[^} ])"#).unwrap(); - regex.replace_all(&content, "$1\r\n$2").to_string() + let regex = regex::Regex::new(r"(\n\s*})(\r\n\s{0,8}[^} ])").unwrap(); + regex.replace_all(content, "$1\r\n$2").to_string() }) } @@ -683,8 +673,7 @@ fn validate_openapi_spec_version(spec_version: &str) -> Result<()> { Ok(()) } else { Err(Error::UnexpectedFormat(format!( - "invalid version: {}", - spec_version + "invalid version: {spec_version}" ))) } } @@ -704,10 +693,9 @@ pub fn validate_openapi(spec: &OpenAPI) -> Result<()> { // operation ID is only used once in the document. item.iter().try_for_each(|(_, o)| { if let Some(oid) = o.operation_id.as_ref() { - if !opids.insert(oid.to_string()) { + if !opids.insert(oid.clone()) { return Err(Error::UnexpectedFormat(format!( - "duplicate operation ID: {}", - oid, + "duplicate operation ID: {oid}", ))); } } else { diff --git a/progenitor-impl/src/method.rs b/progenitor-impl/src/method.rs index 8bf1d0fe..e2dc73fb 100644 --- a/progenitor-impl/src/method.rs +++ b/progenitor-impl/src/method.rs @@ -56,21 +56,21 @@ impl std::str::FromStr for HttpMethod { "head" => Ok(Self::Head), "patch" => Ok(Self::Patch), "trace" => Ok(Self::Trace), - _ => Err(Error::InternalError(format!("bad method: {}", s))), + _ => Err(Error::InternalError(format!("bad method: {s}"))), } } } impl HttpMethod { fn as_str(&self) -> &'static str { match self { - HttpMethod::Get => "get", - HttpMethod::Put => "put", - HttpMethod::Post => "post", - HttpMethod::Delete => "delete", - HttpMethod::Options => "options", - HttpMethod::Head => "head", - HttpMethod::Patch => "patch", - HttpMethod::Trace => "trace", + Self::Get => "get", + Self::Put => "put", + Self::Post => "post", + Self::Delete => "delete", + Self::Options => "options", + Self::Head => "head", + Self::Patch => "patch", + Self::Trace => "trace", } } } @@ -120,11 +120,10 @@ pub enum OperationParameterKind { impl OperationParameterKind { fn is_required(&self) -> bool { match self { - OperationParameterKind::Path => true, - OperationParameterKind::Query(required) => *required, - OperationParameterKind::Header(required) => *required, + Self::Path => true, + Self::Query(required) | Self::Header(required) => *required, // TODO may be optional - OperationParameterKind::Body(_) => true, + Self::Body(_) => true, } } fn is_optional(&self) -> bool { @@ -151,8 +150,7 @@ impl FromStr for BodyContentType { "application/x-www-form-urlencoded" => Ok(Self::FormUrlencoded), "text/plain" | "text/x-markdown" => Ok(Self::Text(String::from(&s[..offset]))), _ => Err(Error::UnexpectedFormat(format!( - "unexpected content type: {}", - s + "unexpected content type: {s}" ))), } } @@ -206,39 +204,34 @@ pub(crate) enum OperationResponseStatus { impl OperationResponseStatus { fn to_value(&self) -> u16 { match self { - OperationResponseStatus::Code(code) => { + Self::Code(code) => { assert!(*code < 1000); *code } - OperationResponseStatus::Range(range) => { + Self::Range(range) => { assert!(*range < 10); *range * 100 } - OperationResponseStatus::Default => 1000, + Self::Default => 1000, } } pub fn is_success_or_default(&self) -> bool { matches!( self, - OperationResponseStatus::Default - | OperationResponseStatus::Code(101) - | OperationResponseStatus::Code(200..=299) - | OperationResponseStatus::Range(2) + Self::Default | Self::Code(101 | 200..=299) | Self::Range(2) ) } pub fn is_error_or_default(&self) -> bool { matches!( self, - OperationResponseStatus::Default - | OperationResponseStatus::Code(400..=599) - | OperationResponseStatus::Range(4..=5) + Self::Default | Self::Code(400..=599) | Self::Range(4..=5) ) } pub fn is_default(&self) -> bool { - matches!(self, OperationResponseStatus::Default) + matches!(self, Self::Default) } } @@ -265,17 +258,17 @@ pub(crate) enum OperationResponseKind { impl OperationResponseKind { pub fn into_tokens(self, type_space: &TypeSpace) -> TokenStream { match self { - OperationResponseKind::Type(ref type_id) => { + Self::Type(ref type_id) => { let type_name = type_space.get_type(type_id).unwrap().ident(); quote! { #type_name } } - OperationResponseKind::None => { + Self::None => { quote! { () } } - OperationResponseKind::Raw => { + Self::Raw => { quote! { ByteStream } } - OperationResponseKind::Upgrade => { + Self::Upgrade => { quote! { reqwest::Upgraded } } } @@ -392,13 +385,13 @@ impl Generator { }) } openapiv3::Parameter::Path { style, .. } => Err(Error::UnexpectedFormat( - format!("unsupported style of path parameter {:#?}", style,), + format!("unsupported style of path parameter {style:#?}",), )), openapiv3::Parameter::Query { style, .. } => Err(Error::UnexpectedFormat( - format!("unsupported style of query parameter {:#?}", style,), + format!("unsupported style of query parameter {style:#?}",), )), cookie @ openapiv3::Parameter::Cookie { .. } => Err(Error::UnexpectedFormat( - format!("cookie parameters are not supported {:#?}", cookie,), + format!("cookie parameters are not supported {cookie:#?}",), )), } }) @@ -522,15 +515,14 @@ impl Generator { status_code: OperationResponseStatus::Code(101), typ: OperationResponseKind::Upgrade, description: None, - }) + }); } let dropshot_paginated = self.dropshot_pagination_data(operation, ¶ms, &responses); if dropshot_websocket && dropshot_paginated.is_some() { return Err(Error::InvalidExtension(format!( - "conflicting extensions in {:?}", - operation_id + "conflicting extensions in {operation_id:?}" ))); } @@ -609,7 +601,7 @@ impl Generator { success: success_type, error: error_type, body, - } = self.method_sig_body(method, quote! { Self }, quote! { self }, has_inner)?; + } = self.method_sig_body(method, "e! { Self }, quote! { self }, has_inner)?; let method_impl = quote! { #[doc = #doc_comment] @@ -766,7 +758,7 @@ impl Generator { fn method_sig_body( &self, method: &OperationMethod, - client_type: TokenStream, + client_type: &TokenStream, client_value: TokenStream, has_inner: bool, ) -> Result { @@ -1010,12 +1002,11 @@ impl Generator { OperationResponseKind::Upgrade => { if response.status_code == OperationResponseStatus::Default { return quote! {}; // catch-all handled below - } else { - todo!( - "non-default error response handling for \ - upgrade requests is not yet implemented" - ); } + todo!( + "non-default error response handling for \ + upgrade requests is not yet implemented" + ); } }; @@ -1051,9 +1042,10 @@ impl Generator { } }; - let inner = match has_inner { - true => quote! { &#client_value.inner, }, - false => quote! {}, + let inner = if has_inner { + quote! { &#client_value.inner, } + } else { + quote! {} }; let pre_hook = self.settings.pre_hook.as_ref().map(|hook| { quote! { @@ -1233,8 +1225,7 @@ impl Generator { .filter(|param| { matches!( (param.api_name.as_str(), ¶m.kind), - ("page_token", OperationParameterKind::Query(false)) - | ("limit", OperationParameterKind::Query(false)) + ("page_token" | "limit", OperationParameterKind::Query(false)) ) }) .count() @@ -1283,9 +1274,9 @@ impl Generator { }; let typ = self.type_space.get_type(success_response).ok()?; - let details = match typ.details() { - typify::TypeDetails::Struct(details) => details, - _ => return None, + + let typify::TypeDetails::Struct(details) = typ.details() else { + return None; }; let properties = details.properties().collect::>(); @@ -1655,7 +1646,7 @@ impl Generator { body, } = self.method_sig_body( method, - quote! { super::Client }, + "e! { super::Client }, quote! { #client_ident }, has_inner, )?; @@ -1663,7 +1654,7 @@ impl Generator { let send_doc = format!( "Sends a `{}` request to `{}`", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, ); let send_impl = quote! { #[doc = #send_doc] @@ -1724,7 +1715,7 @@ impl Generator { let stream_doc = format!( "Streams `{}` requests to `{}`", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, ); quote! { @@ -1813,7 +1804,7 @@ impl Generator { let struct_doc = match (tag_style, method.tags.len(), method.tags.first()) { (TagStyle::Merged, _, _) | (TagStyle::Separate, 0, _) => { let ty = format!("Client::{}", method.operation_id); - format!("Builder for [`{}`]\n\n[`{}`]: super::{}", ty, ty, ty,) + format!("Builder for [`{ty}`]\n\n[`{ty}`]: super::{ty}",) } (TagStyle::Separate, 1, Some(tag)) => { let ty = format!( @@ -1821,7 +1812,7 @@ impl Generator { sanitize(tag, Case::Pascal), method.operation_id ); - format!("Builder for [`{}`]\n\n[`{}`]: super::{}", ty, ty, ty,) + format!("Builder for [`{ty}`]\n\n[`{ty}`]: super::{ty}",) } (TagStyle::Separate, _, _) => { format!( @@ -1848,7 +1839,7 @@ impl Generator { sanitize(tag, Case::Pascal), method.operation_id, ); - format!("[`{}`]: super::{}", ty, ty) + format!("[`{ty}`]: super::{ty}") }) .collect::>() .join("\n"), @@ -1888,8 +1879,7 @@ impl Generator { .params .iter() .map(|param| format!("\n .{}({})", param.name, param.name)) - .collect::>() - .join(""); + .collect::(); let eg = format!( "\ @@ -1943,7 +1933,7 @@ impl Generator { let mut base = Vec::new(); let mut ext = BTreeMap::new(); - methods.iter().for_each(|method| { + for method in methods { let BuilderImpl { doc, sig, body } = self.builder_helper(method); if method.tags.is_empty() { @@ -1971,7 +1961,7 @@ impl Generator { .push((trait_sig.clone(), impl_body.clone())); }); } - }); + } let base_impl = (!base.is_empty()).then(|| { quote! { @@ -2091,8 +2081,7 @@ impl Generator { )), } if enumeration.is_empty() => Ok(()), _ => Err(Error::UnexpectedFormat(format!( - "invalid schema for application/octet-stream: {:?}", - schema + "invalid schema for application/octet-stream: {schema:?}" ))), }?; OperationParameterType::RawBody @@ -2125,8 +2114,7 @@ impl Generator { )), } if enumeration.is_empty() => Ok(()), _ => Err(Error::UnexpectedFormat(format!( - "invalid schema for {}: {:?}", - content_type, schema + "invalid schema for {content_type}: {schema:?}" ))), }?; OperationParameterType::RawBody @@ -2174,7 +2162,7 @@ fn make_doc_comment(method: &OperationMethod) -> String { buf.push_str(&format!( "Sends a `{}` request to `{}`\n\n", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, )); if method @@ -2213,7 +2201,7 @@ fn make_stream_doc_comment(method: &OperationMethod) -> String { buf.push_str(&format!( "Sends repeated `{}` requests to `{}` until there are no more results.\n\n", method.method.as_str().to_ascii_uppercase(), - method.path.to_string(), + method.path, )); if method @@ -2260,11 +2248,11 @@ fn sort_params(raw_params: &mut [OperationParameter], names: &[String]) { let a_index = names .iter() .position(|x| x == a_name) - .unwrap_or_else(|| panic!("{} missing from path", a_name)); + .unwrap_or_else(|| panic!("{a_name} missing from path")); let b_index = names .iter() .position(|x| x == b_name) - .unwrap_or_else(|| panic!("{} missing from path", b_name)); + .unwrap_or_else(|| panic!("{b_name} missing from path")); a_index.cmp(&b_index) } (OperationParameterKind::Path, OperationParameterKind::Query(_)) => Ordering::Less, @@ -2318,7 +2306,7 @@ impl ParameterDataExt for openapiv3::ParameterData { match &self.format { openapiv3::ParameterSchemaOrContent::Schema(s) => Ok(s), openapiv3::ParameterSchemaOrContent::Content(c) => Err(Error::UnexpectedFormat( - format!("unexpected content {:#?}", c), + format!("unexpected content {c:#?}"), )), } } diff --git a/progenitor-impl/src/template.rs b/progenitor-impl/src/template.rs index 47f3cc85..45a98d72 100644 --- a/progenitor-impl/src/template.rs +++ b/progenitor-impl/src/template.rs @@ -35,7 +35,7 @@ impl PathTemplate { "{}", rename .get(&n) - .expect(&format!("missing path name mapping {}", n)), + .unwrap_or_else(|| panic!("missing path name mapping {n}")), ); Some(quote! { encode_path(&#param.to_string()) @@ -69,7 +69,7 @@ impl PathTemplate { Component::Parameter(_) => "[^/]*".to_string(), }) .collect::(); - format!("^{}$", inner) + format!("^{inner}$") } pub fn as_wildcard_param(&self, param: &str) -> String { @@ -82,7 +82,7 @@ impl PathTemplate { Component::Parameter(_) => ".*".to_string(), }) .collect::(); - format!("^{}$", inner) + format!("^{inner}$") } } @@ -133,10 +133,7 @@ pub fn parse(t: &str) -> Result { State::Parameter => { if c == '}' { if a.contains('/') || a.contains('{') { - return Err(Error::InvalidPath(format!( - "invalid parameter name {:?}", - a, - ))); + return Err(Error::InvalidPath(format!("invalid parameter name {a:?}",))); } components.push(Component::Parameter(a)); a = String::new(); @@ -158,18 +155,21 @@ pub fn parse(t: &str) -> Result { Ok(PathTemplate { components }) } -impl ToString for PathTemplate { - fn to_string(&self) -> std::string::String { - self.components - .iter() - .map(|component| match component { - Component::Constant(s) => s.clone(), - Component::Parameter(s) => format!("{{{}}}", s), - }) - .fold(String::new(), |a, b| a + &b) +impl std::fmt::Display for PathTemplate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for component in &self.components { + match component { + // Write the string slice directly to the formatter + Component::Constant(s) => f.write_str(s)?, + + // Use write! macro to handle formatting (escaping braces) + // Note: {{ outputs a literal '{' and }} outputs a literal '}' + Component::Parameter(s) => write!(f, "{{{s}}}")?, + } + } + Ok(()) } } - #[cfg(test)] mod tests { use std::collections::HashMap; @@ -178,7 +178,7 @@ mod tests { #[test] fn basic() { - let trials = vec![ + let trials = [ ( "/info", "/info", @@ -258,20 +258,20 @@ mod tests { ), ]; - for (path, expect_string, want) in trials.iter() { + for (path, expect_string, want) in &trials { match parse(path) { Ok(t) => { assert_eq!(&t, want); assert_eq!(t.to_string().as_str(), *expect_string); } - Err(e) => panic!("path {} {}", path, e), + Err(e) => panic!("path {path} {e}"), } } } #[test] fn names() { - let trials = vec![ + let trials = [ ("/info", vec![]), ("/measure/{number}", vec!["number".to_string()]), ( @@ -280,10 +280,10 @@ mod tests { ), ]; - for (path, want) in trials.iter() { + for (path, want) in &trials { match parse(path) { Ok(t) => assert_eq!(&t.names(), want), - Err(e) => panic!("path {} {}", path, e), + Err(e) => panic!("path {path} {e}"), } } } diff --git a/progenitor-impl/src/to_schema.rs b/progenitor-impl/src/to_schema.rs index 359d5bc6..9a4ada2b 100644 --- a/progenitor-impl/src/to_schema.rs +++ b/progenitor-impl/src/to_schema.rs @@ -50,10 +50,10 @@ where impl Convert for openapiv3::ReferenceOr { fn convert(&self) -> schemars::schema::Schema { match self { - openapiv3::ReferenceOr::Reference { reference } => { + Self::Reference { reference } => { schemars::schema::SchemaObject::new_ref(reference.clone()).into() } - openapiv3::ReferenceOr::Item(schema) => schema.convert(), + Self::Item(schema) => schema.convert(), } } } @@ -117,7 +117,7 @@ impl Convert for openapiv3::Schema { examples: example.into_iter().collect::>(), }; - let metadata = Some(Box::new(metadata)).reduce(); + let metadata = Some(Box::new(metadata)).simplify(); let extensions = extensions.into_iter().collect(); match &self.schema_kind { @@ -137,7 +137,7 @@ impl Convert for openapiv3::Schema { min_length: min_length.convert(), pattern: pattern.clone(), })) - .reduce(), + .simplify(), extensions, ..Default::default() }, @@ -170,7 +170,7 @@ impl Convert for openapiv3::Schema { minimum, exclusive_minimum, })) - .reduce(), + .simplify(), extensions, ..Default::default() } @@ -207,7 +207,7 @@ impl Convert for openapiv3::Schema { minimum, exclusive_minimum, })) - .reduce(), + .simplify(), extensions, ..Default::default() } @@ -231,7 +231,7 @@ impl Convert for openapiv3::Schema { additional_properties: additional_properties.convert(), property_names: None, })) - .reduce(), + .simplify(), extensions, ..Default::default() }, @@ -254,7 +254,7 @@ impl Convert for openapiv3::Schema { unique_items: if *unique_items { Some(true) } else { None }, contains: None, })) - .reduce(), + .simplify(), extensions, ..Default::default() }, @@ -594,7 +594,7 @@ impl Convert for openapiv3::Schema { (None, _) => None, }; } - }; + } // If we have exactly one type, and it's null, and we have // subschemas that means that we must have had a bunch of @@ -627,9 +627,9 @@ where { fn convert(&self) -> Option { match self { - openapiv3::VariantOrUnknownOrEmpty::Item(i) => Some(i.convert()), - openapiv3::VariantOrUnknownOrEmpty::Unknown(s) => Some(s.clone()), - openapiv3::VariantOrUnknownOrEmpty::Empty => None, + Self::Item(i) => Some(i.convert()), + Self::Unknown(s) => Some(s.clone()), + Self::Empty => None, } } } @@ -637,11 +637,11 @@ where impl Convert for openapiv3::StringFormat { fn convert(&self) -> String { match self { - openapiv3::StringFormat::Date => "date", - openapiv3::StringFormat::DateTime => "date-time", - openapiv3::StringFormat::Password => "password", - openapiv3::StringFormat::Byte => "byte", - openapiv3::StringFormat::Binary => "binary", + Self::Date => "date", + Self::DateTime => "date-time", + Self::Password => "password", + Self::Byte => "byte", + Self::Binary => "binary", } .to_string() } @@ -650,8 +650,8 @@ impl Convert for openapiv3::StringFormat { impl Convert for openapiv3::NumberFormat { fn convert(&self) -> String { match self { - openapiv3::NumberFormat::Float => "float", - openapiv3::NumberFormat::Double => "double", + Self::Float => "float", + Self::Double => "double", } .to_string() } @@ -660,8 +660,8 @@ impl Convert for openapiv3::NumberFormat { impl Convert for openapiv3::IntegerFormat { fn convert(&self) -> String { match self { - openapiv3::IntegerFormat::Int32 => "int32", - openapiv3::IntegerFormat::Int64 => "int64", + Self::Int32 => "int32", + Self::Int64 => "int64", } .to_string() } @@ -750,8 +750,8 @@ impl Convert> for Option { } } -impl Convert> for Option { - fn convert(&self) -> Option { +impl Convert for Option { + fn convert(&self) -> Self { *self } } @@ -791,22 +791,22 @@ where impl Convert for openapiv3::AdditionalProperties { fn convert(&self) -> schemars::schema::Schema { match self { - openapiv3::AdditionalProperties::Any(b) => schemars::schema::Schema::Bool(*b), - openapiv3::AdditionalProperties::Schema(schema) => schema.convert(), + Self::Any(b) => schemars::schema::Schema::Bool(*b), + Self::Schema(schema) => schema.convert(), } } } -trait OptionReduce { - fn reduce(self) -> Self; +trait OptionSimplify { + fn simplify(self) -> Self; } // If an Option is `Some` of it's default value, we can simplify that to `None` -impl OptionReduce for Option +impl OptionSimplify for Option where T: Default + PartialEq + std::fmt::Debug, { - fn reduce(self) -> Self { + fn simplify(self) -> Self { match &self { Some(s) if s != &T::default() => self, _ => None, diff --git a/progenitor-impl/src/util.rs b/progenitor-impl/src/util.rs index 170d00ac..f3028ef6 100644 --- a/progenitor-impl/src/util.rs +++ b/progenitor-impl/src/util.rs @@ -18,14 +18,14 @@ pub(crate) trait ComponentLookup: Sized { impl ReferenceOrExt for openapiv3::ReferenceOr { fn item<'a>(&'a self, components: &'a Option) -> Result<&'a T> { match self { - ReferenceOr::Item(item) => Ok(item), - ReferenceOr::Reference { reference } => { + Self::Item(item) => Ok(item), + Self::Reference { reference } => { let idx = reference.rfind('/').unwrap(); let key = &reference[idx + 1..]; let parameters = T::get_components(components.as_ref().unwrap()); parameters .get(key) - .unwrap_or_else(|| panic!("key {} is missing", key)) + .unwrap_or_else(|| panic!("key {key} is missing")) .item(components) } } @@ -100,14 +100,14 @@ pub(crate) fn sanitize(input: &str, case: Case) -> String { let out = match out.chars().next() { None => to_case("x"), Some(c) if is_xid_start(c) => out, - Some(_) => format!("_{}", out), + Some(_) => format!("_{out}"), }; // Make sure the string is a valid Rust identifier. if syn::parse_str::(&out).is_ok() { out } else { - format!("{}_", out) + format!("{out}_") } } @@ -126,6 +126,6 @@ pub(crate) fn unique_ident_from( return ident; } - name.insert_str(0, "_"); + name.insert(0, '_'); } } diff --git a/progenitor-impl/tests/test_output.rs b/progenitor-impl/tests/test_output.rs index 010315d9..42be8398 100644 --- a/progenitor-impl/tests/test_output.rs +++ b/progenitor-impl/tests/test_output.rs @@ -17,17 +17,16 @@ where P: AsRef + std::clone::Clone + std::fmt::Debug, { let mut f = File::open(p.clone()).unwrap(); - match serde_json::from_reader(f) { - Ok(json_value) => json_value, - _ => { - f = File::open(p).unwrap(); - serde_yaml::from_reader(f).unwrap() - } + if let Ok(json_value) = serde_json::from_reader(f) { + json_value + } else { + f = File::open(p).unwrap(); + serde_yaml::from_reader(f).unwrap() } } fn generate_formatted(generator: &mut Generator, spec: &OpenAPI) -> String { - let content = generator.generate_tokens(&spec).unwrap(); + let content = generator.generate_tokens(spec).unwrap(); reformat_code(content) } @@ -38,7 +37,7 @@ fn reformat_code(content: TokenStream) -> String { wrap_comments: Some(true), ..Default::default() }; - space_out_items(rustfmt_wrapper::rustfmt_config(rustfmt_config, content).unwrap()).unwrap() + space_out_items(&rustfmt_wrapper::rustfmt_config(rustfmt_config, content).unwrap()).unwrap() } #[track_caller] @@ -53,7 +52,7 @@ fn verify_apis(openapi_file: &str) { let mut generator = Generator::default(); let output = generate_formatted(&mut generator, &spec); expectorate::assert_contents( - format!("tests/output/src/{}_positional.rs", openapi_stem), + format!("tests/output/src/{openapi_stem}_positional.rs"), &output, ); @@ -76,7 +75,7 @@ fn verify_apis(openapi_file: &str) { ); let output = generate_formatted(&mut generator, &spec); expectorate::assert_contents( - format!("tests/output/src/{}_builder.rs", openapi_stem), + format!("tests/output/src/{openapi_stem}_builder.rs"), &output, ); @@ -89,7 +88,7 @@ fn verify_apis(openapi_file: &str) { ); let output = generate_formatted(&mut generator, &spec); expectorate::assert_contents( - format!("tests/output/src/{}_builder_tagged.rs", openapi_stem), + format!("tests/output/src/{openapi_stem}_builder_tagged.rs"), &output, ); @@ -99,7 +98,7 @@ fn verify_apis(openapi_file: &str) { .unwrap(); let output = reformat_code(tokens); - expectorate::assert_contents(format!("tests/output/src/{}_cli.rs", openapi_stem), &output); + expectorate::assert_contents(format!("tests/output/src/{openapi_stem}_cli.rs"), &output); // httpmock generation. let code = generator @@ -116,9 +115,9 @@ fn verify_apis(openapi_file: &str) { ) .unwrap(); - let output = progenitor_impl::space_out_items(output).unwrap(); + let output = progenitor_impl::space_out_items(&output).unwrap(); expectorate::assert_contents( - format!("tests/output/src/{}_httpmock.rs", openapi_stem), + format!("tests/output/src/{openapi_stem}_httpmock.rs"), &output, ); } @@ -165,7 +164,7 @@ fn test_cli_gen() { #[test] fn test_nexus_with_different_timeout() { - const OPENAPI_FILE: &'static str = "nexus.json"; + const OPENAPI_FILE: &str = "nexus.json"; let mut in_path = PathBuf::from("../sample_openapi"); in_path.push(OPENAPI_FILE); @@ -176,7 +175,7 @@ fn test_nexus_with_different_timeout() { let mut generator = Generator::new(GenerationSettings::default().with_timeout(75)); let output = generate_formatted(&mut generator, &spec); expectorate::assert_contents( - format!("tests/output/src/{}_with_timeout.rs", openapi_stem), + format!("tests/output/src/{openapi_stem}_with_timeout.rs"), &output, ); } diff --git a/progenitor-impl/tests/test_specific.rs b/progenitor-impl/tests/test_specific.rs index ba29aa6c..5b945dd9 100644 --- a/progenitor-impl/tests/test_specific.rs +++ b/progenitor-impl/tests/test_specific.rs @@ -24,7 +24,7 @@ fn generate_formatted(generator: &mut Generator, spec: &OpenAPI) -> String { wrap_comments: Some(true), ..Default::default() }; - space_out_items(rustfmt_wrapper::rustfmt_config(rustfmt_config, content).unwrap()).unwrap() + space_out_items(&rustfmt_wrapper::rustfmt_config(rustfmt_config, content).unwrap()).unwrap() } #[allow(dead_code)] @@ -83,7 +83,7 @@ fn test_renamed_parameters() { expectorate::assert_contents( format!("tests/output/src/{}.rs", "test_renamed_parameters"), &output, - ) + ); } #[endpoint { @@ -114,7 +114,7 @@ fn test_freeform_response() { expectorate::assert_contents( format!("tests/output/src/{}.rs", "test_freeform_response"), &output, - ) + ); } #[derive(Deserialize, JsonSchema)] diff --git a/progenitor-macro/src/lib.rs b/progenitor-macro/src/lib.rs index e15eae3f..2c61d544 100644 --- a/progenitor-macro/src/lib.rs +++ b/progenitor-macro/src/lib.rs @@ -268,7 +268,7 @@ fn is_crate(s: &str) -> bool { fn open_file(path: PathBuf, span: proc_macro2::Span) -> Result { File::open(path.clone()).map_err(|e| { let path_str = path.to_string_lossy(); - syn::Error::new(span, format!("couldn't read file {}: {}", path_str, e)) + syn::Error::new(span, format!("couldn't read file {path_str}: {e}")) }) } @@ -353,7 +353,7 @@ fn do_generate_api(item: TokenStream) -> Result { _ => { f = open_file(path.clone(), spec.span())?; serde_yaml::from_reader(f).map_err(|e| { - syn::Error::new(spec.span(), format!("failed to parse {}: {}", path_str, e)) + syn::Error::new(spec.span(), format!("failed to parse {path_str}: {e}")) })? } }; diff --git a/progenitor/tests/build_keeper.rs b/progenitor/tests/build_keeper.rs index 8ed63341..1c558805 100644 --- a/progenitor/tests/build_keeper.rs +++ b/progenitor/tests/build_keeper.rs @@ -7,8 +7,8 @@ mod positional { let _ = Client::new("").enrol( "auth token", &types::EnrolBody { - host: "".to_string(), - key: "".to_string(), + host: String::new(), + key: String::new(), }, ); } @@ -26,8 +26,8 @@ mod builder_untagged { .enrol() .authorization("") .body(types::EnrolBody { - host: "".to_string(), - key: "".to_string(), + host: String::new(), + key: String::new(), }) .send(); } @@ -45,8 +45,8 @@ mod builder_tagged { .enrol() .authorization("") .body(types::EnrolBody { - host: "".to_string(), - key: "".to_string(), + host: String::new(), + key: String::new(), }) .send(); }