1
0
Fork 0

support non-json return types

This commit is contained in:
Cyborus 2024-02-09 15:28:09 -05:00
parent cb1f2d9ae8
commit c51e080214
No known key found for this signature in database
3 changed files with 110 additions and 50 deletions

View file

@ -250,7 +250,7 @@ fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<ResponseT
.http_codes .http_codes
.iter() .iter()
.filter(|(k, _)| k.starts_with("2")) .filter(|(k, _)| k.starts_with("2"))
.map(|(_, v)| response_ref_type_name(spec, v)) .map(|(_, v)| response_ref_type_name(spec, v, op))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let mut iter = responses.into_iter(); let mut iter = responses.into_iter();
let mut response = iter let mut response = iter
@ -266,6 +266,7 @@ fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<ResponseT
fn response_ref_type_name( fn response_ref_type_name(
spec: &OpenApiV2, spec: &OpenApiV2,
schema: &MaybeRef<Response>, schema: &MaybeRef<Response>,
op: &Operation,
) -> eyre::Result<ResponseType> { ) -> eyre::Result<ResponseType> {
let response = schema.deref(spec)?; let response = schema.deref(spec)?;
let mut ty = ResponseType::default(); let mut ty = ResponseType::default();
@ -282,9 +283,38 @@ fn response_ref_type_name(
}; };
ty.headers = Some(format!("{}Headers", parent_name)); ty.headers = Some(format!("{}Headers", parent_name));
} }
let produces = op
.produces
.as_deref()
.or_else(|| spec.produces.as_deref())
.unwrap_or_default();
// can't use .contains() because Strings
let produces_json = produces.iter().any(|i| matches!(&**i, "application/json"));
let produces_text = produces.iter().any(|i| i.starts_with("text/"));
let produces_other = produces
.iter()
.any(|i| !matches!(&**i, "application/json") && !i.starts_with("text/"));
match (produces_json, produces_text, produces_other) {
(true, false, false) => {
if let Some(schema) = &response.schema { if let Some(schema) = &response.schema {
ty.kind = Some(ResponseKind::Json);
ty.body = Some(crate::schema_ref_type_name(spec, schema)?); ty.body = Some(crate::schema_ref_type_name(spec, schema)?);
}; };
}
(false, _, true) => {
ty.kind = Some(ResponseKind::Bytes);
ty.body = Some("Vec<u8>".into());
}
(false, true, false) => {
ty.kind = Some(ResponseKind::Text);
ty.body = Some("String".into());
}
(false, false, false) => {
ty.kind = None;
ty.body = None;
}
_ => eyre::bail!("produces value unsupported. json: {produces_json}, text: {produces_text}, other: {produces_other}"),
};
Ok(ty) Ok(ty)
} }
@ -401,7 +431,7 @@ fn create_method_response(spec: &OpenApiV2, op: &Operation) -> eyre::Result<Stri
let mut has_empty = false; let mut has_empty = false;
let mut only_empty = true; let mut only_empty = true;
for (code, res) in &op.responses.http_codes { for (code, res) in &op.responses.http_codes {
let response = response_ref_type_name(spec, res)?; let response = response_ref_type_name(spec, res, op)?;
if !code.starts_with("2") { if !code.starts_with("2") {
continue; continue;
} }
@ -446,21 +476,28 @@ fn create_method_response(spec: &OpenApiV2, op: &Operation) -> eyre::Result<Stri
} }
}; };
handlers.extend(header_handler); handlers.extend(header_handler);
let body_handler = match &res.schema { let body_handler = match fn_ret.kind {
Some(schema) if crate::schema_is_string(spec, schema)? => { Some(ResponseKind::Text) => {
if optional { if optional {
Some("Some(response.text().await?)") Some("Some(response.text().await?)")
} else { } else {
Some("response.text().await?") Some("response.text().await?")
} }
} }
Some(_) => { Some(ResponseKind::Json) => {
if optional { if optional {
Some("Some(response.json().await?)") Some("Some(response.json().await?)")
} else { } else {
Some("response.json().await?") Some("response.json().await?")
} }
} }
Some(ResponseKind::Bytes) => {
if optional {
Some("Some(response.bytes().await?[..].to_vec())")
} else {
Some("response.bytes().await?[..].to_vec()")
}
}
None => { None => {
if optional { if optional {
Some("None") Some("None")
@ -495,27 +532,50 @@ fn create_method_response(spec: &OpenApiV2, op: &Operation) -> eyre::Result<Stri
struct ResponseType { struct ResponseType {
headers: Option<String>, headers: Option<String>,
body: Option<String>, body: Option<String>,
kind: Option<ResponseKind>,
} }
impl ResponseType { impl ResponseType {
fn merge(self, other: Self) -> eyre::Result<Self> { fn merge(self, other: Self) -> eyre::Result<Self> {
let mut new = Self::default(); let headers = match (self.headers, other.headers) {
match (self.headers, other.headers) {
(Some(a), Some(b)) if a != b => eyre::bail!("incompatible header types in response"), (Some(a), Some(b)) if a != b => eyre::bail!("incompatible header types in response"),
(Some(a), None) => new.headers = Some(format!("Option<{a}>")), (Some(a), None) => Some(format!("Option<{a}>")),
(None, Some(b)) => new.headers = Some(format!("Option<{b}>")), (None, Some(b)) => Some(format!("Option<{b}>")),
(a, b) => new.headers = a.or(b), (a, b) => a.or(b),
}; };
match (self.body.as_deref(), other.body.as_deref()) { let body = match (self.body.as_deref(), other.body.as_deref()) {
(Some(a), Some(b)) if a != b => eyre::bail!("incompatible header types in response"), (Some(a), Some(b)) if a != b => eyre::bail!("incompatible header types in response"),
(Some(a), Some("()") | None) => new.body = Some(format!("Option<{a}>")), (Some(a), Some("()") | None) => Some(format!("Option<{a}>")),
(Some("()") | None, Some(b)) => new.body = Some(format!("Option<{b}>")), (Some("()") | None, Some(b)) => Some(format!("Option<{b}>")),
(_, _) => new.body = self.body.or(other.body), (_, _) => self.body.or(other.body),
};
use ResponseKind::*;
let kind = match (self.kind, other.kind) {
(a, None) => a,
(None, b) => b,
(Some(Json), Some(Json)) => Some(Json),
(Some(Bytes), Some(Bytes)) => Some(Bytes),
(Some(Bytes), Some(Text)) => Some(Bytes),
(Some(Text), Some(Bytes)) => Some(Bytes),
(Some(Text), Some(Text)) => Some(Text),
_ => eyre::bail!("incompatible response kinds"),
};
let new = Self {
headers,
body,
kind,
}; };
Ok(new) Ok(new)
} }
} }
#[derive(Debug, Clone, Copy)]
enum ResponseKind {
Json,
Text,
Bytes,
}
impl std::fmt::Display for ResponseType { impl std::fmt::Display for ResponseType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut tys = Vec::new(); let mut tys = Vec::new();

View file

@ -1436,13 +1436,13 @@ impl crate::Forgejo {
owner: &str, owner: &str,
repo: &str, repo: &str,
archive: &str, archive: &str,
) -> Result<(), ForgejoError> { ) -> Result<Vec<u8>, ForgejoError> {
let request = self let request = self
.get(&format!("repos/{owner}/{repo}/archive/{archive}")) .get(&format!("repos/{owner}/{repo}/archive/{archive}"))
.build()?; .build()?;
let response = self.execute(request).await?; let response = self.execute(request).await?;
match response.status().as_u16() { match response.status().as_u16() {
200 => Ok(()), 200 => Ok(response.bytes().await?[..].to_vec()),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
} }
} }
@ -2625,7 +2625,7 @@ impl crate::Forgejo {
let response = self.execute(request).await?; let response = self.execute(request).await?;
match response.status().as_u16() { match response.status().as_u16() {
200 => Ok(Some(response.json().await?)), 200 => Ok(Some(response.json().await?)),
204 => Ok(None), 204 => Ok(Some(response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
} }
} }
@ -2671,7 +2671,7 @@ impl crate::Forgejo {
let response = self.execute(request).await?; let response = self.execute(request).await?;
match response.status().as_u16() { match response.status().as_u16() {
200 => Ok(Some(response.json().await?)), 200 => Ok(Some(response.json().await?)),
204 => Ok(None), 204 => Ok(Some(response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
} }
} }
@ -3274,7 +3274,7 @@ impl crate::Forgejo {
let response = self.execute(request).await?; let response = self.execute(request).await?;
match response.status().as_u16() { match response.status().as_u16() {
200 => Ok(Some(response.json().await?)), 200 => Ok(Some(response.json().await?)),
204 => Ok(None), 204 => Ok(Some(response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
} }
} }
@ -9635,8 +9635,8 @@ pub mod structs {
impl TryFrom<&reqwest::header::HeaderMap> for ChangedFileListHeaders { impl TryFrom<&reqwest::header::HeaderMap> for ChangedFileListHeaders {
type Error = StructureError; type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let x_has_more = value let x_has_more = map
.get("X-HasMore") .get("X-HasMore")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9644,7 +9644,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_page = value let x_page = map
.get("X-Page") .get("X-Page")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9652,7 +9652,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_page_count = value let x_page_count = map
.get("X-PageCount") .get("X-PageCount")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9660,7 +9660,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_per_page = value let x_per_page = map
.get("X-PerPage") .get("X-PerPage")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9668,7 +9668,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_total = value let x_total = map
.get("X-Total") .get("X-Total")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9697,8 +9697,8 @@ pub mod structs {
impl TryFrom<&reqwest::header::HeaderMap> for CommitListHeaders { impl TryFrom<&reqwest::header::HeaderMap> for CommitListHeaders {
type Error = StructureError; type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let x_has_more = value let x_has_more = map
.get("X-HasMore") .get("X-HasMore")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9706,7 +9706,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_page = value let x_page = map
.get("X-Page") .get("X-Page")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9714,7 +9714,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_page_count = value let x_page_count = map
.get("X-PageCount") .get("X-PageCount")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9722,7 +9722,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_per_page = value let x_per_page = map
.get("X-PerPage") .get("X-PerPage")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9730,7 +9730,7 @@ pub mod structs {
.map_err(|_| StructureError::HeaderParseFailed) .map_err(|_| StructureError::HeaderParseFailed)
}) })
.transpose()?; .transpose()?;
let x_total = value let x_total = map
.get("X-Total") .get("X-Total")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9756,15 +9756,15 @@ pub mod structs {
impl TryFrom<&reqwest::header::HeaderMap> for ErrorHeaders { impl TryFrom<&reqwest::header::HeaderMap> for ErrorHeaders {
type Error = StructureError; type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let message = value let message = map
.get("message") .get("message")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string()) Ok(s.to_string())
}) })
.transpose()?; .transpose()?;
let url = value let url = map
.get("url") .get("url")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9783,15 +9783,15 @@ pub mod structs {
impl TryFrom<&reqwest::header::HeaderMap> for ForbiddenHeaders { impl TryFrom<&reqwest::header::HeaderMap> for ForbiddenHeaders {
type Error = StructureError; type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let message = value let message = map
.get("message") .get("message")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string()) Ok(s.to_string())
}) })
.transpose()?; .transpose()?;
let url = value let url = map
.get("url") .get("url")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9810,15 +9810,15 @@ pub mod structs {
impl TryFrom<&reqwest::header::HeaderMap> for InvalidTopicsErrorHeaders { impl TryFrom<&reqwest::header::HeaderMap> for InvalidTopicsErrorHeaders {
type Error = StructureError; type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let invalid_topics = value let invalid_topics = map
.get("invalidTopics") .get("invalidTopics")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.split(",").map(|s| s.to_string()).collect::<Vec<_>>()) Ok(s.split(",").map(|s| s.to_string()).collect::<Vec<_>>())
}) })
.transpose()?; .transpose()?;
let message = value let message = map
.get("message") .get("message")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
@ -9840,15 +9840,15 @@ pub mod structs {
impl TryFrom<&reqwest::header::HeaderMap> for ValidationErrorHeaders { impl TryFrom<&reqwest::header::HeaderMap> for ValidationErrorHeaders {
type Error = StructureError; type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let message = value let message = map
.get("message") .get("message")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string()) Ok(s.to_string())
}) })
.transpose()?; .transpose()?;
let url = value let url = map
.get("url") .get("url")
.map(|s| -> Result<_, _> { .map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;

View file

@ -1,11 +1,9 @@
{ {
"consumes": [ "consumes": [
"application/json", "application/json"
"text/plain"
], ],
"produces": [ "produces": [
"application/json", "application/json"
"text/html"
], ],
"schemes": [ "schemes": [
"http", "http",
@ -3603,7 +3601,9 @@
"/repos/{owner}/{repo}/archive/{archive}": { "/repos/{owner}/{repo}/archive/{archive}": {
"get": { "get": {
"produces": [ "produces": [
"application/json" "application/octet-stream",
"application/zip,",
"application/gzip"
], ],
"tags": [ "tags": [
"repository" "repository"