summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCyborus <cyborus@cyborus.xyz>2024-02-09 21:28:09 +0100
committerCyborus <cyborus@cyborus.xyz>2024-02-09 21:29:34 +0100
commitc51e0802143d4cbd546296e9c2889361a9a3cf1d (patch)
tree398d7adbe60c3ff54d4c032976781184338b75cf
parentstrongly typed header returns (diff)
downloadforgejo-api-c51e0802143d4cbd546296e9c2889361a9a3cf1d.tar.xz
forgejo-api-c51e0802143d4cbd546296e9c2889361a9a3cf1d.zip
support non-json return types
-rw-r--r--generator/src/methods.rs92
-rw-r--r--src/generated.rs58
-rw-r--r--swagger.v1.json10
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"