1
0
Fork 0

strongly typed header returns

This commit is contained in:
Cyborus 2024-02-09 10:58:44 -05:00
parent 311e17e3ba
commit cb1f2d9ae8
No known key found for this signature in database
5 changed files with 408 additions and 12 deletions

View file

@ -1818,13 +1818,13 @@ impl crate::Forgejo {
owner: &str,
repo: &str,
query: RepoGetAllCommitsQuery,
) -> Result<(reqwest::header::HeaderMap, Vec<Commit>), ForgejoError> {
) -> Result<(CommitListHeaders, Vec<Commit>), ForgejoError> {
let request = self
.get(&format!("repos/{owner}/{repo}/commits?{query}"))
.build()?;
let response = self.execute(request).await?;
match response.status().as_u16() {
200 => Ok((response.headers().clone(), response.json().await?)),
200 => Ok((response.headers().try_into()?, response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
}
}
@ -4471,7 +4471,7 @@ impl crate::Forgejo {
repo: &str,
index: u64,
query: RepoGetPullRequestCommitsQuery,
) -> Result<(reqwest::header::HeaderMap, Vec<Commit>), ForgejoError> {
) -> Result<(CommitListHeaders, Vec<Commit>), ForgejoError> {
let request = self
.get(&format!(
"repos/{owner}/{repo}/pulls/{index}/commits?{query}"
@ -4479,7 +4479,7 @@ impl crate::Forgejo {
.build()?;
let response = self.execute(request).await?;
match response.status().as_u16() {
200 => Ok((response.headers().clone(), response.json().await?)),
200 => Ok((response.headers().try_into()?, response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
}
}
@ -4495,13 +4495,13 @@ impl crate::Forgejo {
repo: &str,
index: u64,
query: RepoGetPullRequestFilesQuery,
) -> Result<(reqwest::header::HeaderMap, Vec<ChangedFile>), ForgejoError> {
) -> Result<(ChangedFileListHeaders, Vec<ChangedFile>), ForgejoError> {
let request = self
.get(&format!("repos/{owner}/{repo}/pulls/{index}/files?{query}"))
.build()?;
let response = self.execute(request).await?;
match response.status().as_u16() {
200 => Ok((response.headers().clone(), response.json().await?)),
200 => Ok((response.headers().try_into()?, response.json().await?)),
_ => Err(ForgejoError::UnexpectedStatusCode(response.status())),
}
}
@ -7217,6 +7217,7 @@ impl crate::Forgejo {
}
use structs::*;
pub mod structs {
use crate::StructureError;
/// APIError is an api error with a message
///
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
@ -9623,6 +9624,241 @@ pub mod structs {
pub title: Option<String>,
}
pub struct ChangedFileListHeaders {
pub x_has_more: Option<bool>,
pub x_page: Option<u64>,
pub x_page_count: Option<u64>,
pub x_per_page: Option<u64>,
pub x_total: Option<u64>,
}
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
.get("X-HasMore")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<bool>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_page = value
.get("X-Page")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_page_count = value
.get("X-PageCount")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_per_page = value
.get("X-PerPage")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_total = value
.get("X-Total")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
Ok(Self {
x_has_more,
x_page,
x_page_count,
x_per_page,
x_total,
})
}
}
pub struct CommitListHeaders {
pub x_has_more: Option<bool>,
pub x_page: Option<u64>,
pub x_page_count: Option<u64>,
pub x_per_page: Option<u64>,
pub x_total: Option<u64>,
}
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
.get("X-HasMore")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<bool>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_page = value
.get("X-Page")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_page_count = value
.get("X-PageCount")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_per_page = value
.get("X-PerPage")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
let x_total = value
.get("X-Total")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
s.parse::<u64>()
.map_err(|_| StructureError::HeaderParseFailed)
})
.transpose()?;
Ok(Self {
x_has_more,
x_page,
x_page_count,
x_per_page,
x_total,
})
}
}
pub struct ErrorHeaders {
pub message: Option<String>,
pub url: Option<String>,
}
impl TryFrom<&reqwest::header::HeaderMap> for ErrorHeaders {
type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let message = value
.get("message")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string())
})
.transpose()?;
let url = value
.get("url")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string())
})
.transpose()?;
Ok(Self { message, url })
}
}
pub struct ForbiddenHeaders {
pub message: Option<String>,
pub url: Option<String>,
}
impl TryFrom<&reqwest::header::HeaderMap> for ForbiddenHeaders {
type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let message = value
.get("message")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string())
})
.transpose()?;
let url = value
.get("url")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string())
})
.transpose()?;
Ok(Self { message, url })
}
}
pub struct InvalidTopicsErrorHeaders {
pub invalid_topics: Option<Vec<String>>,
pub message: Option<String>,
}
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
.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
.get("message")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string())
})
.transpose()?;
Ok(Self {
invalid_topics,
message,
})
}
}
pub struct ValidationErrorHeaders {
pub message: Option<String>,
pub url: Option<String>,
}
impl TryFrom<&reqwest::header::HeaderMap> for ValidationErrorHeaders {
type Error = StructureError;
fn try_from(value: &reqwest::header::HeaderMap) -> Result<Self, Self::Error> {
let message = value
.get("message")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string())
})
.transpose()?;
let url = value
.get("url")
.map(|s| -> Result<_, _> {
let s = s.to_str().map_err(|_| StructureError::HeaderNotAscii)?;
Ok(s.to_string())
})
.transpose()?;
Ok(Self { message, url })
}
}
pub struct AdminCronListQuery {
pub page: Option<u32>,
pub limit: Option<u32>,

View file

@ -23,7 +23,7 @@ pub enum ForgejoError {
#[error("API key should be ascii")]
KeyNotAscii,
#[error("the response from forgejo was not properly structured")]
BadStructure(#[source] serde_json::Error, String),
BadStructure(#[from] StructureError),
#[error("unexpected status code {} {}", .0.as_u16(), .0.canonical_reason().unwrap_or(""))]
UnexpectedStatusCode(StatusCode),
#[error("{} {}{}", .0.as_u16(), .0.canonical_reason().unwrap_or(""), .1.as_ref().map(|s| format!(": {s}")).unwrap_or_default())]
@ -32,6 +32,21 @@ pub enum ForgejoError {
AuthTooLong,
}
#[derive(thiserror::Error, Debug)]
pub enum StructureError {
#[error("{contents}")]
Serde {
e: serde_json::Error,
contents: String,
},
#[error("failed to find header `{0}`")]
HeaderMissing(&'static str),
#[error("header was not ascii")]
HeaderNotAscii,
#[error("failed to parse header")]
HeaderParseFailed,
}
/// Method of authentication to connect to the Forgejo host with.
pub enum Auth<'a> {
/// Application Access Token. Grants access to scope enabled for the