diff --git a/src/rust/client_gen.rs b/src/rust/client_gen.rs index f203243..7809ab4 100644 --- a/src/rust/client_gen.rs +++ b/src/rust/client_gen.rs @@ -268,7 +268,7 @@ fn match_tag(tag: &Option, path_item: &ReferenceOr) -> bool { fn param_data_to_type(data: &ParameterData, ref_cache: &mut RefCache) -> Result { match &data.format { ParameterSchemaOrContent::Schema(ref_or_schema) => { - ref_or_schema_type(ref_or_schema, ref_cache) + ref_or_schema_type(ref_or_schema, ref_cache, None) } ParameterSchemaOrContent::Content(_) => { Err(Error::unimplemented("Content parameter is not supported.")) @@ -301,79 +301,95 @@ fn request_body_params( "Unexpected ref request body: '{reference}'." ))), ReferenceOr::Item(body) => { - if body.content.len() != 1 { - Err(Error::unimplemented("Content with not exactly 1 option.")) - } else { - let (content_type, media_type) = body.content.first().unwrap(); - - if content_type.starts_with("application/json") { - let schema = match &media_type.schema { - None => Err(Error::unimplemented("JSON content without schema.")), - Some(schema) => Ok(schema), - }; + let (content_type, media_type) = + // TODO: It is in standards that a REST API may support multiple content types + // and we shouldn't fail. Handle this to complete the PR + body.content.first().unwrap(); + + if content_type.starts_with("application/json") { + let schema = match &media_type.schema { + None => Err(Error::unimplemented("JSON content without schema.")), + Some(schema) => Ok(schema), + }; + + let param = Param { + original_name: "".to_string(), + name: "value".to_string(), + tpe: ref_or_schema_type(schema?, ref_cache, Some(content_type))?, + required: body.required, + kind: ParamKind::Body, + }; + + Ok(vec![param]) + } else if content_type == "application/octet-stream" { + Ok(vec![Param { + original_name: "".to_string(), + name: "value".to_string(), + tpe: DataType::Binary, + required: body.required, + kind: ParamKind::Body, + }]) + } else if content_type == "application/x-yaml" { + let schema = match &media_type.schema { + None => Err(Error::unimplemented("YAML content without schema.")), + Some(schema) => Ok(schema), + }; + + let param = Param { + original_name: "".to_string(), + name: "value".to_string(), + tpe: ref_or_schema_type(schema?, ref_cache, Some(content_type))?, + required: body.required, + kind: ParamKind::Body, + }; + + Ok(vec![param]) + } else if content_type == "multipart/form-data" { + match &media_type.schema { + None => Err(Error::unimplemented("Multipart content without schema.")), + Some(schema) => match schema { + ReferenceOr::Reference { reference } => Err(Error::unimplemented(format!( + "Unexpected ref multipart schema: '{reference}'." + ))), + ReferenceOr::Item(schema) => match &schema.schema_kind { + SchemaKind::Type(Type::Object(obj)) => { + fn multipart_param( + name: &str, + required: bool, + schema: &ReferenceOr>, + ref_cache: &mut RefCache, + ) -> Result { + Ok(Param { + original_name: name.to_string(), + name: name.to_case(Case::Snake), + tpe: ref_or_box_schema_type(schema, ref_cache)?, + required, + kind: ParamKind::Multipart, + }) + } - Ok(vec![Param { - original_name: "".to_string(), - name: "value".to_string(), - tpe: ref_or_schema_type(schema?, ref_cache)?, - required: body.required, - kind: ParamKind::Body, - }]) - } else if content_type == "application/octet-stream" { - Ok(vec![Param { - original_name: "".to_string(), - name: "value".to_string(), - tpe: DataType::Binary, - required: body.required, - kind: ParamKind::Body, - }]) - } else if content_type == "multipart/form-data" { - match &media_type.schema { - None => Err(Error::unimplemented("Multipart content without schema.")), - Some(schema) => match schema { - ReferenceOr::Reference { reference } => Err(Error::unimplemented( - format!("Unexpected ref multipart schema: '{reference}'."), + obj.properties + .iter() + .map(|(name, schema)| { + multipart_param( + name, + body.required && obj.required.contains(name), + schema, + ref_cache, + ) + }) + .collect() + } + _ => Err(Error::unimplemented( + "Object schema expected for multipart request body.", )), - ReferenceOr::Item(schema) => match &schema.schema_kind { - SchemaKind::Type(Type::Object(obj)) => { - fn multipart_param( - name: &str, - required: bool, - schema: &ReferenceOr>, - ref_cache: &mut RefCache, - ) -> Result { - Ok(Param { - original_name: name.to_string(), - name: name.to_case(Case::Snake), - tpe: ref_or_box_schema_type(schema, ref_cache)?, - required, - kind: ParamKind::Multipart, - }) - } - - obj.properties - .iter() - .map(|(name, schema)| { - multipart_param( - name, - body.required && obj.required.contains(name), - schema, - ref_cache, - ) - }) - .collect() - } - _ => Err(Error::unimplemented( - "Object schema expected for multipart request body.", - )), - }, }, - } - } else { - Err(Error::unimplemented(format!( - "Request body content type: '{content_type}'." - ))) + }, } + } else { + Err(Error::unimplemented(format!( + "Request body content type: '{content_type}'." + ))) } } } @@ -449,7 +465,7 @@ fn response_type(response: &ReferenceOr, ref_cache: &mut RefCache) -> Some(schema) => Ok(schema), }; - Ok(ref_or_schema_type(schema?, ref_cache)?) + Ok(ref_or_schema_type(schema?, ref_cache, Some(content_type))?) } else if content_type == "application/octet-stream" { Ok(DataType::Binary) } else { @@ -837,7 +853,14 @@ fn render_method_implementation(method: &Method, error_kind: &ErrorKind) -> Rust r#"request = request.header(reqwest::header::CONTENT_TYPE, "application/octet-stream");"#, ) + NewLine - } else { + } else if param.tpe == DataType::Yaml { + line(unit() + "request = request.body(serde_yaml::to_string(" + ¶m.name + ").unwrap_or_default().into_bytes());") + + line( + r#"request = request.header(reqwest::header::CONTENT_TYPE, "application/x-yaml");"#, + ) + NewLine + } + // Not sure why everything else is assumed to be json (previously) + else { line(unit() + "request = request.json(" + ¶m.name + ");") + NewLine } } diff --git a/src/rust/types.rs b/src/rust/types.rs index 23b451e..1f58763 100644 --- a/src/rust/types.rs +++ b/src/rust/types.rs @@ -73,6 +73,7 @@ pub enum DataType { Array(Box), MapOf(Box), Json, + Yaml } pub fn escape_keywords(name: &str) -> String { @@ -143,6 +144,11 @@ impl DataType { let res = rust_name("serde_json::value", "Value"); to_ref(res, top_param) } + + DataType::Yaml => { + let res = rust_name("serde_yaml::value", "Value"); + to_ref(res, top_param) + } } } } @@ -159,7 +165,7 @@ pub fn ref_type_name(reference: &str, ref_cache: &mut RefCache) -> Result Result { +fn schema_type(schema: &Schema, ref_cache: &mut RefCache, content_type: Option<&str>) -> Result { match &schema.schema_kind { SchemaKind::Type(tpe) => match tpe { Type::String(string_type) => { @@ -213,7 +219,7 @@ fn schema_type(schema: &Schema, ref_cache: &mut RefCache) -> Result { "Object parameter with Any additional_properties is not supported.", )), AdditionalProperties::Schema(element_schema) => Ok(DataType::MapOf( - Box::new(ref_or_schema_type(element_schema, ref_cache)?), + Box::new(ref_or_schema_type(element_schema, ref_cache, None)?), )), } } else { @@ -238,17 +244,30 @@ fn schema_type(schema: &Schema, ref_cache: &mut RefCache) -> Result { SchemaKind::AllOf { .. } => Err(Error::unimplemented("AllOf parameter is not supported.")), SchemaKind::AnyOf { .. } => Err(Error::unimplemented("AnyOf parameter is not supported.")), SchemaKind::Not { .. } => Err(Error::unimplemented("Not parameter is not supported.")), - SchemaKind::Any(_) => Ok(DataType::Json), + SchemaKind::Any(_) => { + if let Some(content_type) = content_type { + if content_type == "application/json" { + Ok(DataType::Json) + } else if content_type == "application/x-yaml" { + Ok(DataType::Yaml) + } else { + Err(Error::unexpected(format!("Cannot resolve the data type for content_type {} with `any` schema-kind", content_type))) + } + } else { + Err(Error::unexpected("Cannot resolve the data type for any schema-kind with no details on content_type")) + } + }, } } pub fn ref_or_schema_type( ref_or_schema: &ReferenceOr, ref_cache: &mut RefCache, + content_type: Option<&str> ) -> Result { match ref_or_schema { ReferenceOr::Reference { reference } => ref_type_name(reference, ref_cache), - ReferenceOr::Item(schema) => schema_type(schema, ref_cache), + ReferenceOr::Item(schema) => schema_type(schema, ref_cache, content_type), } } @@ -258,6 +277,6 @@ pub fn ref_or_box_schema_type( ) -> Result { match ref_or_schema { ReferenceOr::Reference { reference } => ref_type_name(reference, ref_cache), - ReferenceOr::Item(schema) => schema_type(schema, ref_cache), + ReferenceOr::Item(schema) => schema_type(schema, ref_cache, None), } }