diff options
author | Cyborus <cyborus@cyborus.xyz> | 2024-02-09 21:28:09 +0100 |
---|---|---|
committer | Cyborus <cyborus@cyborus.xyz> | 2024-02-09 21:29:34 +0100 |
commit | c51e0802143d4cbd546296e9c2889361a9a3cf1d (patch) | |
tree | 398d7adbe60c3ff54d4c032976781184338b75cf | |
parent | strongly typed header returns (diff) | |
download | forgejo-api-c51e0802143d4cbd546296e9c2889361a9a3cf1d.tar.xz forgejo-api-c51e0802143d4cbd546296e9c2889361a9a3cf1d.zip |
support non-json return types
-rw-r--r-- | generator/src/methods.rs | 92 | ||||
-rw-r--r-- | src/generated.rs | 58 | ||||
-rw-r--r-- | swagger.v1.json | 10 |
3 files changed, 110 insertions, 50 deletions
diff --git a/generator/src/methods.rs b/generator/src/methods.rs index 1cfd276..45c9490 100644 --- a/generator/src/methods.rs +++ b/generator/src/methods.rs @@ -250,7 +250,7 @@ fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<ResponseT .http_codes .iter() .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<_>, _>>()?; let mut iter = responses.into_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( spec: &OpenApiV2, schema: &MaybeRef<Response>, + op: &Operation, ) -> eyre::Result<ResponseType> { let response = schema.deref(spec)?; let mut ty = ResponseType::default(); @@ -282,8 +283,37 @@ fn response_ref_type_name( }; ty.headers = Some(format!("{}Headers", parent_name)); } - if let Some(schema) = &response.schema { - ty.body = Some(crate::schema_ref_type_name(spec, schema)?); + 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 { + ty.kind = Some(ResponseKind::Json); + 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) } @@ -401,7 +431,7 @@ fn create_method_response(spec: &OpenApiV2, op: &Operation) -> eyre::Result<Stri let mut has_empty = false; let mut only_empty = true; 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") { continue; } @@ -446,21 +476,28 @@ fn create_method_response(spec: &OpenApiV2, op: &Operation) -> eyre::Result<Stri } }; handlers.extend(header_handler); - let body_handler = match &res.schema { - Some(schema) if crate::schema_is_string(spec, schema)? => { + let body_handler = match fn_ret.kind { + Some(ResponseKind::Text) => { if optional { Some("Some(response.text().await?)") } else { Some("response.text().await?") } } - Some(_) => { + Some(ResponseKind::Json) => { if optional { Some("Some(response.json().await?)") } else { Some("response.json().await?") } } + Some(ResponseKind::Bytes) => { + if optional { + Some("Some(response.bytes().await?[..].to_vec())") + } else { + Some("response.bytes().await?[..].to_vec()") + } + } None => { if optional { Some("None") @@ -495,27 +532,50 @@ fn create_method_response(spec: &OpenApiV2, op: &Operation) -> eyre::Result<Stri struct ResponseType { headers: Option<String>, body: Option<String>, + kind: Option<ResponseKind>, } impl ResponseType { fn merge(self, other: Self) -> eyre::Result<Self> { - let mut new = Self::default(); - match (self.headers, other.headers) { + let headers = match (self.headers, other.headers) { (Some(a), Some(b)) if a != b => eyre::bail!("incompatible header types in response"), - (Some(a), None) => new.headers = Some(format!("Option<{a}>")), - (None, Some(b)) => new.headers = Some(format!("Option<{b}>")), - (a, b) => new.headers = a.or(b), + (Some(a), None) => Some(format!("Option<{a}>")), + (None, Some(b)) => Some(format!("Option<{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("()") | None) => new.body = Some(format!("Option<{a}>")), - (Some("()") | None, Some(b)) => new.body = Some(format!("Option<{b}>")), - (_, _) => new.body = self.body.or(other.body), + (Some(a), Some("()") | None) => Some(format!("Option<{a}>")), + (Some("()") | None, Some(b)) => Some(format!("Option<{b}>")), + (_, _) => 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) } } +#[derive(Debug, Clone, Copy)] +enum ResponseKind { + Json, + Text, + Bytes, +} + impl std::fmt::Display for ResponseType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut tys = Vec::new(); diff --git a/src/generated.rs b/src/generated.rs index 544b4d6..17f0b01 100644 --- a/src/generated.rs +++ b/src/generated.rs @@ -1436,13 +1436,13 @@ impl crate::Forgejo { owner: &str, repo: &str, archive: &str, - ) -> Result<(), ForgejoError> { + ) -> Result<Vec<u8>, ForgejoError> { let request = self .get(&format!("repos/{owner}/{repo}/archive/{archive}")) .build()?; let response = self.execute(request).await?; match response.status().as_u16() { - 200 => Ok(()), + 200 => Ok(response.bytes().await?[..].to_vec()), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), } } @@ -2625,7 +2625,7 @@ impl crate::Forgejo { let response = self.execute(request).await?; match response.status().as_u16() { 200 => Ok(Some(response.json().await?)), - 204 => Ok(None), + 204 => Ok(Some(response.json().await?)), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), } } @@ -2671,7 +2671,7 @@ impl crate::Forgejo { let response = self.execute(request).await?; match response.status().as_u16() { 200 => Ok(Some(response.json().await?)), - 204 => Ok(None), + 204 => Ok(Some(response.json().await?)), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), } } @@ -3274,7 +3274,7 @@ impl crate::Forgejo { let response = self.execute(request).await?; match response.status().as_u16() { 200 => Ok(Some(response.json().await?)), - 204 => Ok(None), + 204 => Ok(Some(response.json().await?)), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), } } @@ -9635,8 +9635,8 @@ pub mod structs { impl TryFrom<&reqwest::header::HeaderMap> for ChangedFileListHeaders { type Error = StructureError; - fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { - let x_has_more = value + fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { + let x_has_more = map .get("X-HasMore") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9644,7 +9644,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_page = value + let x_page = map .get("X-Page") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9652,7 +9652,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_page_count = value + let x_page_count = map .get("X-PageCount") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9660,7 +9660,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_per_page = value + let x_per_page = map .get("X-PerPage") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9668,7 +9668,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_total = value + let x_total = map .get("X-Total") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9697,8 +9697,8 @@ pub mod structs { impl TryFrom<&reqwest::header::HeaderMap> for CommitListHeaders { type Error = StructureError; - fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { - let x_has_more = value + fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { + let x_has_more = map .get("X-HasMore") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9706,7 +9706,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_page = value + let x_page = map .get("X-Page") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9714,7 +9714,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_page_count = value + let x_page_count = map .get("X-PageCount") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9722,7 +9722,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_per_page = value + let x_per_page = map .get("X-PerPage") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9730,7 +9730,7 @@ pub mod structs { .map_err(|_| StructureError::HeaderParseFailed) }) .transpose()?; - let x_total = value + let x_total = map .get("X-Total") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9756,15 +9756,15 @@ pub mod structs { impl TryFrom<&reqwest::header::HeaderMap> for ErrorHeaders { type Error = StructureError; - fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { - let message = value + fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { + let message = map .get("message") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; Ok(s.to_string()) }) .transpose()?; - let url = value + let url = map .get("url") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9783,15 +9783,15 @@ pub mod structs { impl TryFrom<&reqwest::header::HeaderMap> for ForbiddenHeaders { type Error = StructureError; - fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { - let message = value + fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { + let message = map .get("message") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; Ok(s.to_string()) }) .transpose()?; - let url = value + let url = map .get("url") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9810,15 +9810,15 @@ pub mod structs { impl TryFrom<&reqwest::header::HeaderMap> for InvalidTopicsErrorHeaders { type Error = StructureError; - fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { - let invalid_topics = value + fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { + let invalid_topics = map .get("invalidTopics") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; Ok(s.split(",").map(|s| s.to_string()).collect::<Vec<_>>()) }) .transpose()?; - let message = value + let message = map .get("message") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; @@ -9840,15 +9840,15 @@ pub mod structs { impl TryFrom<&reqwest::header::HeaderMap> for ValidationErrorHeaders { type Error = StructureError; - fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { - let message = value + fn try_from(map: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> { + let message = map .get("message") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; Ok(s.to_string()) }) .transpose()?; - let url = value + let url = map .get("url") .map(|s| -> Result<_, _> { let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?; diff --git a/swagger.v1.json b/swagger.v1.json index fa4880f..88a9f0b 100644 --- a/swagger.v1.json +++ b/swagger.v1.json @@ -1,11 +1,9 @@ { "consumes": [ - "application/json", - "text/plain" + "application/json" ], "produces": [ - "application/json", - "text/html" + "application/json" ], "schemes": [ "http", @@ -3603,7 +3601,9 @@ "/repos/{owner}/{repo}/archive/{archive}": { "get": { "produces": [ - "application/json" + "application/octet-stream", + "application/zip,", + "application/gzip" ], "tags": [ "repository" |