diff --git a/generator/src/structs.rs b/generator/src/structs.rs index 3449f5f..4c78fac 100644 --- a/generator/src/structs.rs +++ b/generator/src/structs.rs @@ -63,7 +63,7 @@ pub fn create_struct_for_definition( crate::schema_subtype_name(spec, name, prop_name, value, &mut field_ty)?; crate::schema_subtypes(spec, name, prop_name, value, &mut subtypes)?; } - if field_name.ends_with("url") && field_name != "ssh_url" && field_ty == "String" { + if field_name.ends_with("url") && field_ty == "String" { field_ty = "url::Url".into() } if field_ty == name { @@ -73,7 +73,16 @@ pub fn create_struct_for_definition( field_ty = format!("Option<{field_ty}>") } if field_ty == "Option" { - fields.push_str("#[serde(deserialize_with = \"crate::none_if_blank_url\")]\n"); + if field_name == "ssh_url" { + fields.push_str( + "#[serde(deserialize_with = \"crate::deserialize_optional_ssh_url\")]\n", + ); + } else { + fields.push_str("#[serde(deserialize_with = \"crate::none_if_blank_url\")]\n"); + } + } + if field_ty == "url::Url" && field_name == "ssh_url" { + fields.push_str("#[serde(deserialize_with = \"crate::deserialize_ssh_url\")]\n"); } if field_ty == "time::OffsetDateTime" { fields.push_str("#[serde(with = \"time::serde::rfc3339\")]\n"); @@ -106,8 +115,8 @@ pub fn create_struct_for_definition( } } - if let Some(additonal_schema) = &schema.additional_properties { - let prop_ty = crate::schema_ref_type_name(spec, additonal_schema)?; + if let Some(additional_schema) = &schema.additional_properties { + let prop_ty = crate::schema_ref_type_name(spec, additional_schema)?; fields.push_str("#[serde(flatten)]\n"); fields.push_str("pub additional: BTreeMap, pub repo_transfer: Option, pub size: Option, - pub ssh_url: Option, + #[serde(deserialize_with = "crate::deserialize_optional_ssh_url")] + pub ssh_url: Option, pub stars_count: Option, pub template: Option, #[serde(with = "time::serde::rfc3339::option")] diff --git a/src/lib.rs b/src/lib.rs index 90d1137..c86ef64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ use reqwest::{Client, Request, StatusCode}; +use serde::{Deserialize, Deserializer}; use soft_assert::*; use url::Url; use zeroize::Zeroize; @@ -254,6 +255,31 @@ fn none_if_blank_url<'de, D: serde::Deserializer<'de>>( deserializer.deserialize_str(EmptyUrlVisitor) } +#[allow(dead_code)] // not used yet, but it might appear in the future +fn deserialize_ssh_url<'de, D, DE>(deserializer: D) -> Result +where + D: Deserializer<'de>, + DE: serde::de::Error, +{ + let raw_url: String = String::deserialize(deserializer).map_err(DE::custom)?; + let url = format!("ssh://{url}", url = raw_url.replace(":", "/")); + Url::parse(url.as_str()).map_err(DE::custom) +} + +fn deserialize_optional_ssh_url<'de, D, DE>(deserializer: D) -> Result, DE> +where + D: Deserializer<'de>, + DE: serde::de::Error, +{ + let raw_url: Option = Option::deserialize(deserializer).map_err(DE::custom)?; + raw_url + .map(|raw_url| { + let url = format!("ssh://{url}", url = raw_url.replace(":", "/")); + Url::parse(url.as_str()).map_err(DE::custom).map(Some) + }) + .unwrap_or(Ok(None)) +} + impl From for structs::MergePullRequestOptionDo { fn from(value: structs::DefaultMergeStyle) -> Self { match value {