1
0
Fork 0

support returning header values

This commit is contained in:
Cyborus 2024-01-21 16:03:11 -05:00
parent 99ae09c8d3
commit 996c722f90
No known key found for this signature in database
2 changed files with 126 additions and 52 deletions

View file

@ -153,52 +153,87 @@ fn query_struct_name(op: &Operation) -> eyre::Result<String> {
Ok(ty) Ok(ty)
} }
fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<String> { fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<ResponseType> {
let mut names = op let mut responses = op
.responses .responses
.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))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let mut iter = responses.into_iter();
let mut response = iter
.next()
.ok_or_else(|| eyre::eyre!("must have at least one response type"))?;
for next in iter {
response = response.merge(next)?;
}
names.sort(); Ok(response)
names.dedup();
let name = match names.len() {
0 => eyre::bail!("no type name found"),
1 => {
let name = names.pop().unwrap();
if name == "empty" {
"()".into()
} else {
name
}
}
2 if names[0] == "empty" || names[1] == "empty" => {
let name = if names[0] == "empty" {
names.remove(1)
} else {
names.remove(0)
};
format!("Option<{name}>")
}
_ => eyre::bail!("too many possible return types"),
};
Ok(name)
} }
fn response_ref_type_name(spec: &OpenApiV2, schema: &MaybeRef<Response>) -> eyre::Result<String> { #[derive(Debug, Default)]
let (name, response) = deref_response(spec, schema)?; struct ResponseType {
if let Some(schema) = &response.schema { headers: Option<String>,
schema_ref_type_name(spec, schema) body: Option<String>,
} else if let Some(name) = name { }
Ok(name.into())
} else { impl ResponseType {
Ok("()".into()) fn merge(self, other: Self) -> eyre::Result<Self> {
let mut new = Self::default();
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),
};
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}>")),
(a, b) => new.body = self.body.or(other.body),
};
Ok(new)
} }
} }
impl std::fmt::Display for ResponseType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut tys = Vec::new();
tys.extend(self.headers.as_deref());
tys.extend(self.body.as_deref());
match tys[..] {
[single] => f.write_str(single),
_ => {
write!(f, "(")?;
for (i, item) in tys.iter().copied().enumerate() {
f.write_str(item)?;
if i + 1 < tys.len() {
write!(f, ", ")?;
}
}
write!(f, ")")?;
Ok(())
}
}
}
}
fn response_ref_type_name(
spec: &OpenApiV2,
schema: &MaybeRef<Response>,
) -> eyre::Result<ResponseType> {
let (_, response) = deref_response(spec, schema)?;
let mut ty = ResponseType::default();
if response.headers.is_some() {
ty.headers = Some("reqwest::header::HeaderMap".into());
}
if let Some(schema) = &response.schema {
ty.body = Some(schema_ref_type_name(spec, schema)?);
};
Ok(ty)
}
fn schema_ref_type_name(spec: &OpenApiV2, schema: &MaybeRef<Schema>) -> eyre::Result<String> { fn schema_ref_type_name(spec: &OpenApiV2, schema: &MaybeRef<Schema>) -> eyre::Result<String> {
let (name, schema) = deref_definition(spec, &schema)?; let (name, schema) = deref_definition(spec, &schema)?;
schema_type_name(spec, name, schema) schema_type_name(spec, name, schema)
@ -503,16 +538,17 @@ fn create_method_response(
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 name = response_ref_type_name(spec, res)?; let response = response_ref_type_name(spec, res)?;
if !code.starts_with("2") { if !code.starts_with("2") {
continue; continue;
} }
if name == "()" || name == "empty" { if matches!(response.body.as_deref(), Some("()") | None) {
has_empty = true; has_empty = true;
} else { } else {
only_empty = false; only_empty = false;
} }
} }
let fn_ret = fn_return_from_op(spec, op)?;
let optional = has_empty && !only_empty; let optional = has_empty && !only_empty;
let mut out = String::new(); let mut out = String::new();
out.push_str("let response = self.execute(request).await?;\n"); out.push_str("let response = self.execute(request).await?;\n");
@ -523,32 +559,70 @@ fn create_method_response(
continue; continue;
} }
out.push_str(code); out.push_str(code);
out.push_str(" => "); out.push_str(" => Ok(");
let handler = match &res.schema { let mut handlers = Vec::new();
let header_handler = match &res.headers {
Some(_) => {
if fn_ret
.headers
.as_ref()
.map(|s| s.starts_with("Option<"))
.unwrap()
{
Some("Some(response.headers().clone())")
} else {
Some("response.headers().clone()")
}
}
None => {
if fn_ret.headers.is_some() {
dbg!(&fn_ret);
panic!();
Some("None")
} else {
None
}
}
};
handlers.extend(header_handler);
let body_handler = match &res.schema {
Some(schema) if schema_is_string(spec, schema)? => { Some(schema) if schema_is_string(spec, schema)? => {
if optional { if optional {
"Ok(Some(response.text().await?))" Some("Some(response.text().await?)")
} else { } else {
"Ok(response.text().await?)" Some("response.text().await?")
} }
} }
Some(_) => { Some(_) => {
if optional { if optional {
"Ok(Some(response.json().await?))" Some("Some(response.json().await?)")
} else { } else {
"Ok(response.json().await?)" Some("response.json().await?")
} }
} }
None => { None => {
if optional { if optional {
"Ok(None)" Some("None")
} else { } else {
"Ok(())" None
} }
} }
}; };
out.push_str(handler); handlers.extend(body_handler);
out.push_str(",\n"); match handlers[..] {
[single] => out.push_str(single),
_ => {
out.push('(');
for (i, item) in handlers.iter().copied().enumerate() {
out.push_str(item);
if i + 1 < handlers.len() {
out.push_str(", ");
}
}
out.push(')');
}
}
out.push_str("),\n");
} }
out.push_str("_ => Err(ForgejoError::UnexpectedStatusCode(response.status()))\n"); out.push_str("_ => Err(ForgejoError::UnexpectedStatusCode(response.status()))\n");
out.push_str("}\n"); out.push_str("}\n");

View file

@ -1864,7 +1864,7 @@ impl crate::Forgejo {
owner: &str, owner: &str,
repo: &str, repo: &str,
query: RepoGetAllCommitsQuery, query: RepoGetAllCommitsQuery,
) -> Result<Vec<Commit>, ForgejoError> { ) -> Result<(reqwest::header::HeaderMap, Vec<Commit>), ForgejoError> {
let request = self let request = self
.get(&format!( .get(&format!(
"repos/{owner}/{repo}/commits?{}", "repos/{owner}/{repo}/commits?{}",
@ -1873,7 +1873,7 @@ impl crate::Forgejo {
.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(response.json().await?), 200 => Ok((response.headers().clone(), response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
} }
} }
@ -4572,7 +4572,7 @@ impl crate::Forgejo {
repo: &str, repo: &str,
index: u64, index: u64,
query: RepoGetPullRequestCommitsQuery, query: RepoGetPullRequestCommitsQuery,
) -> Result<Vec<Commit>, ForgejoError> { ) -> Result<(reqwest::header::HeaderMap, Vec<Commit>), ForgejoError> {
let request = self let request = self
.get(&format!( .get(&format!(
"repos/{owner}/{repo}/pulls/{index}/commits?{}", "repos/{owner}/{repo}/pulls/{index}/commits?{}",
@ -4581,7 +4581,7 @@ impl crate::Forgejo {
.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(response.json().await?), 200 => Ok((response.headers().clone(), response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
} }
} }
@ -4597,7 +4597,7 @@ impl crate::Forgejo {
repo: &str, repo: &str,
index: u64, index: u64,
query: RepoGetPullRequestFilesQuery, query: RepoGetPullRequestFilesQuery,
) -> Result<Vec<ChangedFile>, ForgejoError> { ) -> Result<(reqwest::header::HeaderMap, Vec<ChangedFile>), ForgejoError> {
let request = self let request = self
.get(&format!( .get(&format!(
"repos/{owner}/{repo}/pulls/{index}/files?{}", "repos/{owner}/{repo}/pulls/{index}/files?{}",
@ -4606,7 +4606,7 @@ impl crate::Forgejo {
.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(response.json().await?), 200 => Ok((response.headers().clone(), response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())), _ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
} }
} }