674 lines
24 KiB
Rust
674 lines
24 KiB
Rust
use crate::openapi::*;
|
|
use eyre::WrapErr;
|
|
use heck::ToPascalCase;
|
|
use std::fmt::Write;
|
|
|
|
pub fn create_structs(spec: &OpenApiV2) -> eyre::Result<String> {
|
|
let mut s = String::new();
|
|
s.push_str("use crate::StructureError;");
|
|
s.push_str("use std::collections::BTreeMap;");
|
|
if let Some(definitions) = &spec.definitions {
|
|
for (name, schema) in definitions {
|
|
let strukt = create_struct_for_definition(&spec, name, schema)?;
|
|
s.push_str(&strukt);
|
|
}
|
|
}
|
|
if let Some(responses) = &spec.responses {
|
|
for (name, response) in responses {
|
|
if let Some(headers) = &response.headers {
|
|
let strukt = create_header_struct(name, headers)?;
|
|
s.push_str(&strukt);
|
|
}
|
|
|
|
let tys = create_response_struct(spec, name, response)?;
|
|
s.push_str(&tys);
|
|
}
|
|
}
|
|
for (_, item) in &spec.paths {
|
|
let strukt = create_query_structs_for_path(spec, item)?;
|
|
s.push_str(&strukt);
|
|
|
|
let strukt = create_response_structs(spec, item)?;
|
|
s.push_str(&strukt);
|
|
}
|
|
Ok(s)
|
|
}
|
|
|
|
pub fn create_struct_for_definition(
|
|
spec: &OpenApiV2,
|
|
name: &str,
|
|
schema: &Schema,
|
|
) -> eyre::Result<String> {
|
|
if matches!(schema._type, Some(SchemaType::One(Primitive::Array))) {
|
|
return Ok(String::new());
|
|
}
|
|
|
|
if schema._type == Some(SchemaType::One(Primitive::String)) {
|
|
if let Some(_enum) = &schema._enum {
|
|
return create_enum(name, schema.description.as_deref(), _enum, false);
|
|
}
|
|
}
|
|
|
|
let mut subtypes = Vec::new();
|
|
|
|
let docs = create_struct_docs(schema)?;
|
|
let mut fields = String::new();
|
|
let required = schema.required.as_deref().unwrap_or_default();
|
|
if let Some(properties) = &schema.properties {
|
|
for (prop_name, prop_schema) in properties {
|
|
let prop_ty = crate::schema_ref_type_name(spec, prop_schema)?;
|
|
let field_name = crate::sanitize_ident(prop_name);
|
|
let mut field_ty = prop_ty.clone();
|
|
if let MaybeRef::Value { value } = &prop_schema {
|
|
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_ty == "String" {
|
|
field_ty = "url::Url".into()
|
|
}
|
|
if field_ty == name {
|
|
field_ty = format!("Box<{field_ty}>")
|
|
}
|
|
if !required.contains(prop_name) {
|
|
field_ty = format!("Option<{field_ty}>")
|
|
}
|
|
if field_ty == "Option<url::Url>" {
|
|
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");
|
|
}
|
|
if field_ty == "Option<time::OffsetDateTime>" {
|
|
fields.push_str("#[serde(with = \"time::serde::rfc3339::option\")]\n");
|
|
}
|
|
if let MaybeRef::Value { value } = &prop_schema {
|
|
if let Some(desc) = &value.description {
|
|
for line in desc.lines() {
|
|
fields.push_str("/// ");
|
|
fields.push_str(line);
|
|
fields.push_str("\n/// \n");
|
|
}
|
|
if fields.ends_with("/// \n") {
|
|
fields.truncate(fields.len() - 5);
|
|
}
|
|
}
|
|
}
|
|
if &field_name != prop_name {
|
|
fields.push_str("#[serde(rename = \"");
|
|
fields.push_str(prop_name);
|
|
fields.push_str("\")]\n");
|
|
}
|
|
fields.push_str("pub ");
|
|
fields.push_str(&field_name);
|
|
fields.push_str(": ");
|
|
fields.push_str(&field_ty);
|
|
fields.push_str(",\n");
|
|
}
|
|
}
|
|
|
|
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<String, ");
|
|
fields.push_str(&prop_ty);
|
|
fields.push_str(">,\n");
|
|
}
|
|
|
|
let mut out = format!("{docs}#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]\npub struct {name} {{\n{fields}}}\n\n");
|
|
for subtype in subtypes {
|
|
out.push_str(&subtype);
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
pub fn create_enum(
|
|
name: &str,
|
|
desc: Option<&str>,
|
|
_enum: &[serde_json::Value],
|
|
imp_as_str: bool,
|
|
) -> eyre::Result<String> {
|
|
let mut variants = String::new();
|
|
let mut imp = String::new();
|
|
imp.push_str("match self {");
|
|
let docs = create_struct_docs_str(desc)?;
|
|
for variant in _enum {
|
|
match variant {
|
|
serde_json::Value::String(s) => {
|
|
let variant_name = s.to_pascal_case();
|
|
variants.push_str("#[serde(rename = \"");
|
|
variants.push_str(s);
|
|
variants.push_str("\")]");
|
|
variants.push_str(&variant_name);
|
|
variants.push_str(",\n");
|
|
|
|
writeln!(&mut imp, "{name}::{variant_name} => \"{s}\",")?;
|
|
}
|
|
x => eyre::bail!("cannot create enum variant. expected string, got {x:?}"),
|
|
}
|
|
}
|
|
imp.push_str("}");
|
|
|
|
let strukt = format!(
|
|
"
|
|
{docs}
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub enum {name} {{
|
|
{variants}
|
|
}}"
|
|
);
|
|
let out = if imp_as_str {
|
|
let imp = format!(
|
|
"\n\nimpl {name} {{
|
|
fn as_str(&self) -> &'static str {{
|
|
{imp}
|
|
}}
|
|
}}"
|
|
);
|
|
format!("{strukt} {imp}")
|
|
} else {
|
|
strukt
|
|
};
|
|
Ok(out)
|
|
}
|
|
|
|
fn create_struct_docs(schema: &Schema) -> eyre::Result<String> {
|
|
create_struct_docs_str(schema.description.as_deref())
|
|
}
|
|
|
|
fn create_struct_docs_str(description: Option<&str>) -> eyre::Result<String> {
|
|
let doc = match description {
|
|
Some(desc) => {
|
|
let mut out = String::new();
|
|
for line in desc.lines() {
|
|
out.push_str("/// ");
|
|
out.push_str(line);
|
|
out.push_str("\n/// \n");
|
|
}
|
|
if out.ends_with("/// \n") {
|
|
out.truncate(out.len() - 5);
|
|
}
|
|
out
|
|
}
|
|
None => String::new(),
|
|
};
|
|
Ok(doc)
|
|
}
|
|
|
|
pub fn create_query_structs_for_path(spec: &OpenApiV2, item: &PathItem) -> eyre::Result<String> {
|
|
let mut s = String::new();
|
|
if let Some(op) = &item.get {
|
|
s.push_str(&create_query_struct(spec, op).wrap_err("GET")?);
|
|
}
|
|
if let Some(op) = &item.put {
|
|
s.push_str(&create_query_struct(spec, op).wrap_err("PUT")?);
|
|
}
|
|
if let Some(op) = &item.post {
|
|
s.push_str(&create_query_struct(spec, op).wrap_err("POST")?);
|
|
}
|
|
if let Some(op) = &item.delete {
|
|
s.push_str(&create_query_struct(spec, op).wrap_err("DELETE")?);
|
|
}
|
|
if let Some(op) = &item.options {
|
|
s.push_str(&create_query_struct(spec, op).wrap_err("OPTIONS")?);
|
|
}
|
|
if let Some(op) = &item.head {
|
|
s.push_str(&create_query_struct(spec, op).wrap_err("HEAD")?);
|
|
}
|
|
if let Some(op) = &item.patch {
|
|
s.push_str(&create_query_struct(spec, op).wrap_err("PATCH")?);
|
|
}
|
|
Ok(s)
|
|
}
|
|
|
|
pub fn query_struct_name(op: &Operation) -> eyre::Result<String> {
|
|
let mut ty = op
|
|
.operation_id
|
|
.as_deref()
|
|
.ok_or_else(|| eyre::eyre!("operation did not have id"))?
|
|
.to_pascal_case()
|
|
.replace("o_auth2", "oauth2");
|
|
ty.push_str("Query");
|
|
Ok(ty)
|
|
}
|
|
|
|
fn create_query_struct(spec: &OpenApiV2, op: &Operation) -> eyre::Result<String> {
|
|
let params = match &op.parameters {
|
|
Some(params) => params,
|
|
None => return Ok(String::new()),
|
|
};
|
|
|
|
let op_name = query_struct_name(op)?;
|
|
let mut enums = Vec::new();
|
|
let mut fields = String::new();
|
|
let mut imp = String::new();
|
|
// only derive default if every field is optional
|
|
let mut can_derive_default = true;
|
|
for param in params {
|
|
let param = param.deref(spec)?;
|
|
if let ParameterIn::Query { param: query_param } = ¶m._in {
|
|
let field_name = crate::sanitize_ident(¶m.name);
|
|
let ty = match &query_param {
|
|
NonBodyParameter {
|
|
_type: ParameterType::String,
|
|
_enum: Some(_enum),
|
|
..
|
|
} => {
|
|
let name = format!("{op_name}{}", param.name.to_pascal_case());
|
|
let enum_def = create_enum(&name, None, _enum, true)?;
|
|
enums.push(enum_def);
|
|
name
|
|
}
|
|
NonBodyParameter {
|
|
_type: ParameterType::Array,
|
|
items:
|
|
Some(Items {
|
|
_type: ParameterType::String,
|
|
_enum: Some(_enum),
|
|
..
|
|
}),
|
|
..
|
|
} => {
|
|
let name = format!("{op_name}{}", param.name.to_pascal_case());
|
|
let enum_def = create_enum(&name, None, _enum, true)?;
|
|
enums.push(enum_def);
|
|
format!("Vec<{name}>")
|
|
}
|
|
_ => crate::methods::param_type(query_param, true)?,
|
|
};
|
|
if let Some(desc) = ¶m.description {
|
|
for line in desc.lines() {
|
|
fields.push_str("/// ");
|
|
fields.push_str(line);
|
|
fields.push_str("\n/// \n");
|
|
}
|
|
if fields.ends_with("/// \n") {
|
|
fields.truncate(fields.len() - 5);
|
|
}
|
|
}
|
|
fields.push_str("pub ");
|
|
fields.push_str(&field_name);
|
|
fields.push_str(": ");
|
|
if query_param.required {
|
|
can_derive_default = false;
|
|
fields.push_str(&ty);
|
|
} else {
|
|
fields.push_str("Option<");
|
|
fields.push_str(&ty);
|
|
fields.push_str(">");
|
|
}
|
|
fields.push_str(",\n");
|
|
|
|
let mut handler = String::new();
|
|
if query_param.required {
|
|
writeln!(&mut handler, "let {field_name} = &self.{field_name};")?;
|
|
} else {
|
|
writeln!(
|
|
&mut handler,
|
|
"if let Some({field_name}) = &self.{field_name} {{"
|
|
)?;
|
|
}
|
|
match &query_param._type {
|
|
ParameterType::String => {
|
|
if let Some(_enum) = &query_param._enum {
|
|
writeln!(
|
|
&mut handler,
|
|
"write!(f, \"{}={{}}&\", {}.as_str())?;",
|
|
param.name, field_name,
|
|
)?;
|
|
} else {
|
|
match query_param.format.as_deref() {
|
|
Some("date-time" | "date") => {
|
|
writeln!(
|
|
&mut handler,
|
|
"write!(f, \"{}={{field_name}}&\", field_name = {field_name}.format(&time::format_description::well_known::Rfc3339).unwrap())?;",
|
|
param.name)?;
|
|
}
|
|
_ => {
|
|
writeln!(
|
|
&mut handler,
|
|
"write!(f, \"{}={{{}}}&\")?;",
|
|
param.name,
|
|
field_name.strip_prefix("r#").unwrap_or(&field_name)
|
|
)?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ParameterType::Number | ParameterType::Integer | ParameterType::Boolean => {
|
|
writeln!(
|
|
&mut handler,
|
|
"write!(f, \"{}={{{}}}&\")?;",
|
|
param.name,
|
|
field_name.strip_prefix("r#").unwrap_or(&field_name)
|
|
)?;
|
|
}
|
|
ParameterType::Array => {
|
|
let format = query_param
|
|
.collection_format
|
|
.unwrap_or(CollectionFormat::Csv);
|
|
let item = query_param
|
|
.items
|
|
.as_ref()
|
|
.ok_or_else(|| eyre::eyre!("array must have item type defined"))?;
|
|
let item_pusher = match item._type {
|
|
ParameterType::String => {
|
|
if let Some(_enum) = &item._enum {
|
|
"write!(f, \"{}\", item.as_str())?;"
|
|
} else {
|
|
match query_param.format.as_deref() {
|
|
Some("date-time" | "date") => {
|
|
"write!(f, \"{{date}}\", item.format(&time::format_description::well_known::Rfc3339).unwrap())?;"
|
|
},
|
|
_ => {
|
|
"write!(f, \"{item}\")?;"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ParameterType::Number | ParameterType::Integer | ParameterType::Boolean => {
|
|
"write!(f, \"{item}\")?;"
|
|
}
|
|
ParameterType::Array => {
|
|
eyre::bail!("nested arrays not supported in query");
|
|
}
|
|
ParameterType::File => eyre::bail!("cannot send file in query"),
|
|
};
|
|
match format {
|
|
CollectionFormat::Csv => {
|
|
handler.push_str(&simple_query_array(
|
|
param,
|
|
item_pusher,
|
|
&field_name,
|
|
",",
|
|
)?);
|
|
}
|
|
CollectionFormat::Ssv => {
|
|
handler.push_str(&simple_query_array(
|
|
param,
|
|
item_pusher,
|
|
&field_name,
|
|
" ",
|
|
)?);
|
|
}
|
|
CollectionFormat::Tsv => {
|
|
handler.push_str(&simple_query_array(
|
|
param,
|
|
item_pusher,
|
|
&field_name,
|
|
"\\t",
|
|
)?);
|
|
}
|
|
CollectionFormat::Pipes => {
|
|
handler.push_str(&simple_query_array(
|
|
param,
|
|
item_pusher,
|
|
&field_name,
|
|
"|",
|
|
)?);
|
|
}
|
|
CollectionFormat::Multi => {
|
|
writeln!(&mut handler)?;
|
|
writeln!(&mut handler, "if !{field_name}.is_empty() {{")?;
|
|
writeln!(&mut handler, "for item in {field_name} {{")?;
|
|
writeln!(&mut handler, "write!(f, \"{}=\")?;", param.name)?;
|
|
handler.push_str(item_pusher);
|
|
handler.push('\n');
|
|
writeln!(&mut handler, "write!(f, \"&\")?;")?;
|
|
writeln!(&mut handler, "}}")?;
|
|
writeln!(&mut handler, "}}")?;
|
|
}
|
|
}
|
|
}
|
|
ParameterType::File => eyre::bail!("cannot send file in query"),
|
|
}
|
|
if !query_param.required {
|
|
writeln!(&mut handler, "}}")?;
|
|
}
|
|
imp.push_str(&handler);
|
|
}
|
|
}
|
|
|
|
let derives = if can_derive_default {
|
|
"Debug, Clone, PartialEq, Default"
|
|
} else {
|
|
"Debug, Clone, PartialEq"
|
|
};
|
|
|
|
let result = if fields.is_empty() {
|
|
String::new()
|
|
} else {
|
|
let mut out = format!(
|
|
"
|
|
#[derive({derives})]
|
|
pub struct {op_name} {{
|
|
{fields}
|
|
}}
|
|
|
|
impl std::fmt::Display for {op_name} {{
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{
|
|
{imp}
|
|
Ok(())
|
|
}}
|
|
}}
|
|
"
|
|
);
|
|
|
|
for _enum in enums {
|
|
out.push_str(&_enum);
|
|
}
|
|
|
|
out
|
|
};
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
fn simple_query_array(
|
|
param: &Parameter,
|
|
item_pusher: &str,
|
|
name: &str,
|
|
sep: &str,
|
|
) -> eyre::Result<String> {
|
|
let mut out = String::new();
|
|
|
|
writeln!(
|
|
&mut out,
|
|
"
|
|
if !{name}.is_empty() {{
|
|
write!(f, \"{}=\")?;
|
|
for (item, i) in {name}.iter().enumerate() {{
|
|
{item_pusher}
|
|
if i < {name}.len() - 1 {{
|
|
write!(f, \"{sep}\")?;
|
|
}}
|
|
}}
|
|
write!(f, \"&\")?;
|
|
}}",
|
|
param.name
|
|
)?;
|
|
|
|
Ok(out)
|
|
}
|
|
|
|
fn create_header_struct(
|
|
name: &str,
|
|
headers: &std::collections::BTreeMap<String, Header>,
|
|
) -> eyre::Result<String> {
|
|
let ty_name = format!("{name}Headers").to_pascal_case();
|
|
let mut fields = String::new();
|
|
let mut imp = String::new();
|
|
let mut imp_ret = String::new();
|
|
for (header_name, header) in headers {
|
|
let ty = header_type(header)?;
|
|
let field_name = crate::sanitize_ident(header_name);
|
|
fields.push_str("pub ");
|
|
fields.push_str(&field_name);
|
|
fields.push_str(": Option<");
|
|
fields.push_str(&ty);
|
|
fields.push_str(">,\n");
|
|
|
|
write!(
|
|
&mut imp,
|
|
"
|
|
let {field_name} = map.get(\"{header_name}\").map(|s| -> Result<_, _> {{
|
|
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
|
|
"
|
|
)
|
|
.unwrap();
|
|
match &header._type {
|
|
ParameterType::String => imp.push_str("Ok(s.to_string())"),
|
|
ParameterType::Number => match header.format.as_deref() {
|
|
Some("float") => {
|
|
imp.push_str("s.parse::<f32>().map_err(|_| StructureError::HeaderParseFailed)")
|
|
}
|
|
Some("double") | _ => {
|
|
imp.push_str("s.parse::<f64>()).map_err(|_| StructureError::HeaderParseFailed)")
|
|
}
|
|
},
|
|
ParameterType::Integer => match header.format.as_deref() {
|
|
Some("int64") => {
|
|
imp.push_str("s.parse::<u64>().map_err(|_| StructureError::HeaderParseFailed)")
|
|
}
|
|
Some("int32") | _ => {
|
|
imp.push_str("s.parse::<u32>().map_err(|_| StructureError::HeaderParseFailed)")
|
|
}
|
|
},
|
|
ParameterType::Boolean => {
|
|
imp.push_str("s.parse::<bool>().map_err(|_| StructureError::HeaderParseFailed)")
|
|
}
|
|
ParameterType::Array => {
|
|
let sep = match header.collection_format {
|
|
Some(CollectionFormat::Csv) | None => ",",
|
|
Some(CollectionFormat::Ssv) => " ",
|
|
Some(CollectionFormat::Tsv) => "\\t",
|
|
Some(CollectionFormat::Pipes) => "|",
|
|
Some(CollectionFormat::Multi) => {
|
|
eyre::bail!("multi format not supported in headers")
|
|
}
|
|
};
|
|
let items = header
|
|
.items
|
|
.as_ref()
|
|
.ok_or_else(|| eyre::eyre!("items property must be set for arrays"))?;
|
|
if items._type == ParameterType::String {
|
|
imp.push_str("Ok(");
|
|
}
|
|
imp.push_str("s.split(\"");
|
|
imp.push_str(sep);
|
|
imp.push_str("\").map(|s| ");
|
|
imp.push_str(match items._type {
|
|
ParameterType::String => "s.to_string()).collect::<Vec<_>>())",
|
|
ParameterType::Number => match items.format.as_deref() {
|
|
Some("float") => "s.parse::<f32>()).collect::<Result<Vec<_>, _>>().map_err(|_| StructureError::HeaderParseFailed)",
|
|
Some("double") | _ => "s.parse::<f64>()).collect::<Result<Vec<_>, _>>().map_err(|_| StructureError::HeaderParseFailed)",
|
|
},
|
|
ParameterType::Integer => match items.format.as_deref() {
|
|
Some("int64") => "s.parse::<u64>()).collect::<Result<Vec<_>, _>>().map_err(|_| StructureError::HeaderParseFailed)",
|
|
Some("int32") | _ => "s.parse::<u32>()).collect::<Result<Vec<_>, _>>().map_err(|_| StructureError::HeaderParseFailed)",
|
|
},
|
|
ParameterType::Boolean => "s.parse::<bool>()).collect::<Result<Vec<_>, _>>().map_err(|_| StructureError::HeaderParseFailed)",
|
|
ParameterType::Array => eyre::bail!("nested arrays not supported in headers"),
|
|
ParameterType::File => eyre::bail!("files not supported in headers"),
|
|
});
|
|
}
|
|
ParameterType::File => eyre::bail!("files not supported in headers"),
|
|
}
|
|
imp.push_str("}).transpose()?;");
|
|
|
|
imp_ret.push_str(&field_name);
|
|
imp_ret.push_str(", ");
|
|
}
|
|
|
|
Ok(format!(
|
|
"
|
|
pub struct {ty_name} {{
|
|
{fields}
|
|
}}
|
|
|
|
impl TryFrom<&reqwest::header::HeaderMap> for {ty_name} {{
|
|
type Error = StructureError;
|
|
|
|
fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {{
|
|
{imp}
|
|
Ok(Self {{ {imp_ret} }})
|
|
}}
|
|
}}
|
|
"
|
|
))
|
|
}
|
|
|
|
pub fn header_type(header: &Header) -> eyre::Result<String> {
|
|
crate::methods::param_type_inner(
|
|
&header._type,
|
|
header.format.as_deref(),
|
|
header.items.as_ref(),
|
|
true,
|
|
)
|
|
}
|
|
|
|
pub fn create_response_structs(spec: &OpenApiV2, item: &PathItem) -> eyre::Result<String> {
|
|
let mut s = String::new();
|
|
if let Some(op) = &item.get {
|
|
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("GET")?);
|
|
}
|
|
if let Some(op) = &item.put {
|
|
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("PUT")?);
|
|
}
|
|
if let Some(op) = &item.post {
|
|
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("POST")?);
|
|
}
|
|
if let Some(op) = &item.delete {
|
|
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("DELETE")?);
|
|
}
|
|
if let Some(op) = &item.options {
|
|
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("OPTIONS")?);
|
|
}
|
|
if let Some(op) = &item.head {
|
|
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("HEAD")?);
|
|
}
|
|
if let Some(op) = &item.patch {
|
|
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("PATCH")?);
|
|
}
|
|
Ok(s)
|
|
}
|
|
|
|
pub fn create_response_structs_for_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<String> {
|
|
let mut out = String::new();
|
|
let op_name = op
|
|
.operation_id
|
|
.as_deref()
|
|
.ok_or_else(|| eyre::eyre!("no operation id"))?
|
|
.to_pascal_case();
|
|
for (_, response) in &op.responses.http_codes {
|
|
let response = response.deref(spec)?;
|
|
let tys = create_response_struct(spec, &op_name, response)?;
|
|
out.push_str(&tys);
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
pub fn create_response_struct(
|
|
spec: &OpenApiV2,
|
|
name: &str,
|
|
res: &Response,
|
|
) -> eyre::Result<String> {
|
|
let mut types = Vec::new();
|
|
if let Some(MaybeRef::Value { value }) = &res.schema {
|
|
crate::schema_subtypes(spec, name, "Response", value, &mut types)?;
|
|
}
|
|
let mut out = String::new();
|
|
for ty in types {
|
|
out.push_str(&ty);
|
|
}
|
|
Ok(out)
|
|
}
|