diff options
author | Cyborus <cyborus@noreply.codeberg.org> | 2024-03-20 19:09:31 +0100 |
---|---|---|
committer | Cyborus <cyborus@noreply.codeberg.org> | 2024-03-20 19:09:31 +0100 |
commit | 96b92daf235985c17f19af0f0f19c20055d78f09 (patch) | |
tree | 445b0f8a7db46cfd09080084aab0d9d17dc40b38 /src | |
parent | Merge pull request 'add missing `parent` field to `Repository`' (#36) from pa... (diff) | |
parent | correct tag creation success status code to `201` (diff) | |
download | forgejo-api-96b92daf235985c17f19af0f0f19c20055d78f09.tar.xz forgejo-api-96b92daf235985c17f19af0f0f19c20055d78f09.zip |
Merge pull request 'Auto-generate API' (#38) from autogen into main
Reviewed-on: https://codeberg.org/Cyborus/forgejo-api/pulls/38
Diffstat (limited to 'src')
-rw-r--r-- | src/admin.rs | 502 | ||||
-rw-r--r-- | src/generated/methods.rs | 7226 | ||||
-rw-r--r-- | src/generated/mod.rs | 2 | ||||
-rw-r--r-- | src/generated/structs.rs | 5959 | ||||
-rw-r--r-- | src/issue.rs | 347 | ||||
-rw-r--r-- | src/lib.rs | 257 | ||||
-rw-r--r-- | src/misc.rs | 162 | ||||
-rw-r--r-- | src/notification.rs | 273 | ||||
-rw-r--r-- | src/organization.rs | 30 | ||||
-rw-r--r-- | src/package.rs | 174 | ||||
-rw-r--r-- | src/repository.rs | 743 | ||||
-rw-r--r-- | src/user.rs | 59 |
12 files changed, 13240 insertions, 2494 deletions
diff --git a/src/admin.rs b/src/admin.rs deleted file mode 100644 index 560d6c6..0000000 --- a/src/admin.rs +++ /dev/null @@ -1,502 +0,0 @@ -use super::*; - -use std::collections::BTreeMap; -use std::fmt::Write; - -impl Forgejo { - pub async fn admin_get_crons(&self, query: CronQuery) -> Result<Vec<Cron>, ForgejoError> { - self.get(&query.path()).await - } - - pub async fn admin_run_cron(&self, name: &str) -> Result<(), ForgejoError> { - self.post_unit(&format!("admin/cron/{name}"), &()).await - } - - pub async fn admin_get_emails( - &self, - query: EmailListQuery, - ) -> Result<Vec<Email>, ForgejoError> { - self.get(&query.path()).await - } - - pub async fn admin_search_emails( - &self, - query: EmailSearchQuery, - ) -> Result<Vec<Email>, ForgejoError> { - self.get(&query.path()).await - } - - pub async fn admin_get_hooks(&self, query: HookQuery) -> Result<Vec<Hook>, ForgejoError> { - self.get(&query.path()).await - } - - pub async fn admin_create_hook(&self, opt: CreateHookOption) -> Result<Hook, ForgejoError> { - self.post("admin/hooks", &opt).await - } - - pub async fn admin_get_hook(&self, id: u64) -> Result<Option<Hook>, ForgejoError> { - self.get_opt(&format!("admin/hooks/{id}")).await - } - - pub async fn admin_delete_hook(&self, id: u64) -> Result<(), ForgejoError> { - self.delete(&format!("admin/hooks/{id}")).await - } - - pub async fn admin_edit_hook( - &self, - id: u64, - opt: EditHookOption, - ) -> Result<Hook, ForgejoError> { - self.patch(&format!("admin/hooks/{id}"), &opt).await - } - - pub async fn admin_get_orgs( - &self, - query: AdminOrganizationQuery, - ) -> Result<Vec<Organization>, ForgejoError> { - self.get(&query.path()).await - } - - pub async fn admin_unadopted_repos( - &self, - query: UnadoptedRepoQuery, - ) -> Result<Vec<String>, ForgejoError> { - self.get(&query.path()).await - } - - pub async fn admin_adopt(&self, owner: &str, repo: &str) -> Result<(), ForgejoError> { - self.post(&format!("admin/unadopted/{owner}/{repo}"), &()) - .await - } - - pub async fn admin_delete_unadopted( - &self, - owner: &str, - repo: &str, - ) -> Result<(), ForgejoError> { - self.delete(&format!("admin/unadopted/{owner}/{repo}")) - .await - } - - pub async fn admin_users(&self, query: AdminUserQuery) -> Result<Vec<User>, ForgejoError> { - self.get(&query.path()).await - } - - pub async fn admin_create_user(&self, opt: CreateUserOption) -> Result<User, ForgejoError> { - self.post("admin/users", &opt).await - } - - pub async fn admin_delete_user(&self, user: &str, purge: bool) -> Result<(), ForgejoError> { - self.delete(&format!("admin/users/{user}?purge={purge}")) - .await - } - - pub async fn admin_edit_user( - &self, - user: &str, - opt: CreateUserOption, - ) -> Result<User, ForgejoError> { - self.patch(&format!("admin/users/{user}"), &opt).await - } - - pub async fn admin_add_key( - &self, - user: &str, - opt: CreateKeyOption, - ) -> Result<PublicKey, ForgejoError> { - self.post(&format!("admin/users/{user}/keys"), &opt).await - } - - pub async fn admin_delete_key(&self, user: &str, id: u64) -> Result<(), ForgejoError> { - self.delete(&format!("admin/users/{user}/keys/{id}")).await - } - - pub async fn admin_create_org( - &self, - owner: &str, - opt: CreateOrgOption, - ) -> Result<Organization, ForgejoError> { - self.post(&format!("admin/users/{owner}/orgs"), &opt).await - } - - pub async fn admin_rename_user( - &self, - user: &str, - opt: RenameUserOption, - ) -> Result<(), ForgejoError> { - self.post_unit(&format!("admin/users/{user}/rename"), &opt) - .await - } - - pub async fn admin_create_repo( - &self, - owner: &str, - opt: CreateRepoOption, - ) -> Result<Repository, ForgejoError> { - self.post(&format!("admin/users/{owner}/repos"), &opt).await - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Cron { - pub exec_times: u64, - pub name: String, - #[serde(with = "time::serde::rfc3339")] - pub next: time::OffsetDateTime, - #[serde(with = "time::serde::rfc3339")] - pub prev: time::OffsetDateTime, - pub schedule: String, -} - -#[derive(Default, Debug)] -pub struct CronQuery { - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl CronQuery { - fn path(&self) -> String { - let mut s = String::from("admin/cron?"); - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - s - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Email { - pub email: String, - pub primary: bool, - pub user_id: u64, - pub username: String, - pub verified: bool, -} - -#[derive(Default, Debug)] -pub struct EmailListQuery { - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl EmailListQuery { - fn path(&self) -> String { - let mut s = String::from("admin/emails?"); - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - s - } -} - -#[derive(Default, Debug)] -pub struct EmailSearchQuery { - pub query: String, - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl EmailSearchQuery { - fn path(&self) -> String { - let mut s = String::from("admin/emails/search?"); - if !self.query.is_empty() { - s.push_str("q="); - s.push_str(&self.query); - s.push('&'); - } - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - s - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Hook { - pub active: bool, - pub authorization_header: String, - pub branch_filter: String, - pub config: std::collections::BTreeMap<String, String>, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub events: Vec<String>, - pub id: u64, - #[serde(rename = "type")] - pub _type: HookType, - #[serde(with = "time::serde::rfc3339")] - pub updated_at: time::OffsetDateTime, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)] -#[non_exhaustive] -#[serde(rename_all = "lowercase")] -pub enum HookType { - Forgejo, - Dingtalk, - Discord, - Gitea, - Gogs, - Msteams, - Slack, - Telegram, - Feishu, - Wechatwork, - Packagist, -} - -#[derive(Default, Debug)] -pub struct HookQuery { - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl HookQuery { - fn path(&self) -> String { - let mut s = String::from("admin/hooks?"); - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - s - } -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub struct CreateHookOption { - pub active: Option<bool>, - pub authorization_header: Option<String>, - pub branch_filter: Option<String>, - pub config: CreateHookOptionConfig, - pub events: Vec<String>, - #[serde(rename = "type")] - pub _type: HookType, -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub struct CreateHookOptionConfig { - pub content_type: String, - pub url: Url, - #[serde(flatten)] - pub other: BTreeMap<String, String>, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct EditHookOption { - pub active: Option<bool>, - pub authorization_header: Option<String>, - pub branch_filter: Option<String>, - pub config: Option<BTreeMap<String, String>>, - pub events: Option<Vec<String>>, -} - -#[derive(Default, Debug)] -pub struct AdminOrganizationQuery { - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl AdminOrganizationQuery { - fn path(&self) -> String { - let mut s = String::from("admin/orgs?"); - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - s - } -} - -#[derive(Default, Debug)] -pub struct UnadoptedRepoQuery { - pub page: Option<u32>, - pub limit: Option<u32>, - pub pattern: String, -} - -impl UnadoptedRepoQuery { - fn path(&self) -> String { - let mut s = String::from("admin/unadopted?"); - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if !self.pattern.is_empty() { - s.push_str("pattern="); - s.push_str(&self.pattern); - s.push('&'); - } - s - } -} - -#[derive(Default, Debug)] -pub struct AdminUserQuery { - pub source_id: Option<u64>, - pub login_name: String, - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl AdminUserQuery { - fn path(&self) -> String { - let mut s = String::from("admin/users?"); - if let Some(source_id) = self.source_id { - s.push_str("source_id="); - s.write_fmt(format_args!("{source_id}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if !self.login_name.is_empty() { - s.push_str("login_name="); - s.push_str(&self.login_name); - s.push('&'); - } - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - s - } -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub struct CreateUserOption { - #[serde(with = "time::serde::rfc3339::option")] - pub created_at: Option<time::OffsetDateTime>, - pub email: String, - pub full_name: Option<String>, - pub login_name: Option<String>, - pub must_change_password: bool, - pub password: String, - pub restricted: bool, - pub send_notify: bool, - pub source_id: Option<u64>, - pub username: String, - pub visibility: String, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct EditUserOption { - pub active: Option<bool>, - pub admin: Option<bool>, - pub allow_create_organization: Option<bool>, - pub allow_git_hook: Option<bool>, - pub allow_import_local: Option<bool>, - pub description: Option<String>, - pub email: Option<String>, - pub full_name: Option<String>, - pub location: Option<String>, - pub login_name: Option<String>, - pub max_repo_creation: Option<u64>, - pub must_change_password: Option<bool>, - pub password: Option<String>, - pub prohibit_login: Option<bool>, - pub restricted: Option<bool>, - pub source_id: Option<u64>, - pub visibility: Option<String>, - pub website: Option<String>, -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub struct CreateKeyOption { - pub key: String, - pub read_only: Option<bool>, - pub title: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct PublicKey { - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub fingerprint: String, - pub id: u64, - pub key: String, - pub key_type: String, - pub read_only: Option<bool>, - pub title: String, - pub url: Option<Url>, - pub user: User, -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub struct CreateOrgOption { - pub description: Option<String>, - pub full_name: Option<String>, - pub location: Option<String>, - pub repo_admin_change_team_access: Option<bool>, - pub username: String, - pub visibility: OrgVisibility, - pub website: Option<Url>, -} - -#[derive(serde::Serialize, Debug, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum OrgVisibility { - Public, - Limited, - Private, -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub struct RenameUserOption { - pub new_username: String, -} diff --git a/src/generated/methods.rs b/src/generated/methods.rs new file mode 100644 index 0000000..0f6a97b --- /dev/null +++ b/src/generated/methods.rs @@ -0,0 +1,7226 @@ +use super::structs::*; +use crate::ForgejoError; +use std::collections::BTreeMap; + +impl crate::Forgejo { + /// Returns the Person actor for a user + /// + /// - `user-id`: user ID of the user + pub async fn activitypub_person(&self, user_id: u32) -> Result<ActivityPub, ForgejoError> { + let request = self + .get(&format!("activitypub/user-id/{user_id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Send to the inbox + /// + /// - `user-id`: user ID of the user + pub async fn activitypub_person_inbox(&self, user_id: u32) -> Result<(), ForgejoError> { + let request = self + .post(&format!("activitypub/user-id/{user_id}/inbox")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List cron tasks + /// + pub async fn admin_cron_list( + &self, + query: AdminCronListQuery, + ) -> Result<Vec<Cron>, ForgejoError> { + let request = self.get(&format!("admin/cron?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Run cron task + /// + /// - `task`: task to run + pub async fn admin_cron_run(&self, task: &str) -> Result<(), ForgejoError> { + let request = self.post(&format!("admin/cron/{task}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List all emails + /// + pub async fn admin_get_all_emails( + &self, + query: AdminGetAllEmailsQuery, + ) -> Result<Vec<Email>, ForgejoError> { + let request = self.get(&format!("admin/emails?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Search all emails + /// + pub async fn admin_search_emails( + &self, + query: AdminSearchEmailsQuery, + ) -> Result<Vec<Email>, ForgejoError> { + let request = self.get(&format!("admin/emails/search?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List system's webhooks + /// + pub async fn admin_list_hooks( + &self, + query: AdminListHooksQuery, + ) -> Result<Vec<Hook>, ForgejoError> { + let request = self.get(&format!("admin/hooks?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a hook + /// + /// - `body`: See [`CreateHookOption`] + pub async fn admin_create_hook(&self, body: CreateHookOption) -> Result<Hook, ForgejoError> { + let request = self.post("admin/hooks").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a hook + /// + /// - `id`: id of the hook to get + pub async fn admin_get_hook(&self, id: u64) -> Result<Hook, ForgejoError> { + let request = self.get(&format!("admin/hooks/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a hook + /// + /// - `id`: id of the hook to delete + pub async fn admin_delete_hook(&self, id: u64) -> Result<(), ForgejoError> { + let request = self.delete(&format!("admin/hooks/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a hook + /// + /// - `id`: id of the hook to update + /// - `body`: See [`EditHookOption`] + pub async fn admin_edit_hook( + &self, + id: u64, + body: EditHookOption, + ) -> Result<Hook, ForgejoError> { + let request = self + .patch(&format!("admin/hooks/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List all organizations + /// + pub async fn admin_get_all_orgs( + &self, + query: AdminGetAllOrgsQuery, + ) -> Result<Vec<Organization>, ForgejoError> { + let request = self.get(&format!("admin/orgs?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List unadopted repositories + /// + pub async fn admin_unadopted_list( + &self, + query: AdminUnadoptedListQuery, + ) -> Result<Vec<String>, ForgejoError> { + let request = self.get(&format!("admin/unadopted?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Adopt unadopted files as a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn admin_adopt_repository( + &self, + owner: &str, + repo: &str, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!("admin/unadopted/{owner}/{repo}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete unadopted files + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn admin_delete_unadopted_repository( + &self, + owner: &str, + repo: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("admin/unadopted/{owner}/{repo}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Search users according filter conditions + /// + pub async fn admin_search_users( + &self, + query: AdminSearchUsersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self.get(&format!("admin/users?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a user + /// + /// - `body`: See [`CreateUserOption`] + pub async fn admin_create_user(&self, body: CreateUserOption) -> Result<User, ForgejoError> { + let request = self.post("admin/users").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a user + /// + /// - `username`: username of user to delete + pub async fn admin_delete_user( + &self, + username: &str, + query: AdminDeleteUserQuery, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("admin/users/{username}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit an existing user + /// + /// - `username`: username of user to edit + /// - `body`: See [`EditUserOption`] + pub async fn admin_edit_user( + &self, + username: &str, + body: EditUserOption, + ) -> Result<User, ForgejoError> { + let request = self + .patch(&format!("admin/users/{username}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a public key on behalf of a user + /// + /// - `username`: username of the user + /// - `key`: See [`CreateKeyOption`] + pub async fn admin_create_public_key( + &self, + username: &str, + key: CreateKeyOption, + ) -> Result<PublicKey, ForgejoError> { + let request = self + .post(&format!("admin/users/{username}/keys")) + .json(&key) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a user's public key + /// + /// - `username`: username of user + /// - `id`: id of the key to delete + pub async fn admin_delete_user_public_key( + &self, + username: &str, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("admin/users/{username}/keys/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create an organization + /// + /// - `username`: username of the user that will own the created organization + /// - `organization`: See [`CreateOrgOption`] + pub async fn admin_create_org( + &self, + username: &str, + organization: CreateOrgOption, + ) -> Result<Organization, ForgejoError> { + let request = self + .post(&format!("admin/users/{username}/orgs")) + .json(&organization) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Rename a user + /// + /// - `username`: existing username of user + /// - `body`: See [`RenameUserOption`] + pub async fn admin_rename_user( + &self, + username: &str, + body: RenameUserOption, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!("admin/users/{username}/rename")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a repository on behalf of a user + /// + /// - `username`: username of the user. This user will own the created repository + /// - `repository`: See [`CreateRepoOption`] + pub async fn admin_create_repo( + &self, + username: &str, + repository: CreateRepoOption, + ) -> Result<Repository, ForgejoError> { + let request = self + .post(&format!("admin/users/{username}/repos")) + .json(&repository) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns a list of all gitignore templates + pub async fn list_gitignores_templates(&self) -> Result<Vec<String>, ForgejoError> { + let request = self.get("gitignore/templates").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns information about a gitignore template + /// + /// - `name`: name of the template + pub async fn get_gitignore_template_info( + &self, + name: &str, + ) -> Result<GitignoreTemplateInfo, ForgejoError> { + let request = self.get(&format!("gitignore/templates/{name}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns a list of all label templates + pub async fn list_label_templates(&self) -> Result<Vec<String>, ForgejoError> { + let request = self.get("label/templates").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns all labels in a template + /// + /// - `name`: name of the template + pub async fn get_label_template_info( + &self, + name: &str, + ) -> Result<Vec<LabelTemplate>, ForgejoError> { + let request = self.get(&format!("label/templates/{name}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns a list of all license templates + pub async fn list_license_templates( + &self, + ) -> Result<Vec<LicensesTemplateListEntry>, ForgejoError> { + let request = self.get("licenses").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns information about a license template + /// + /// - `name`: name of the license + pub async fn get_license_template_info( + &self, + name: &str, + ) -> Result<LicenseTemplateInfo, ForgejoError> { + let request = self.get(&format!("licenses/{name}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Render a markdown document as HTML + /// + /// - `body`: See [`MarkdownOption`] + pub async fn render_markdown(&self, body: MarkdownOption) -> Result<String, ForgejoError> { + let request = self.post("markdown").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Render raw markdown as HTML + /// + /// - `body`: Request body to render + + /// See [`String`] + pub async fn render_markdown_raw(&self, body: String) -> Result<String, ForgejoError> { + let request = self.post("markdown/raw").body(body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Render a markup document as HTML + /// + /// - `body`: See [`MarkupOption`] + pub async fn render_markup(&self, body: MarkupOption) -> Result<String, ForgejoError> { + let request = self.post("markup").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns the nodeinfo of the Gitea application + pub async fn get_node_info(&self) -> Result<NodeInfo, ForgejoError> { + let request = self.get("nodeinfo").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List users's notification threads + /// + pub async fn notify_get_list( + &self, + query: NotifyGetListQuery, + ) -> Result<Vec<NotificationThread>, ForgejoError> { + let request = self.get(&format!("notifications?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Mark notification threads as read, pinned or unread + /// + pub async fn notify_read_list( + &self, + query: NotifyReadListQuery, + ) -> Result<Vec<NotificationThread>, ForgejoError> { + let request = self.put(&format!("notifications?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 205 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if unread notifications exist + pub async fn notify_new_available(&self) -> Result<NotificationCount, ForgejoError> { + let request = self.get("notifications/new").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get notification thread by ID + /// + /// - `id`: id of notification thread + pub async fn notify_get_thread(&self, id: &str) -> Result<NotificationThread, ForgejoError> { + let request = self.get(&format!("notifications/threads/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Mark notification thread as read by ID + /// + /// - `id`: id of notification thread + pub async fn notify_read_thread( + &self, + id: &str, + query: NotifyReadThreadQuery, + ) -> Result<NotificationThread, ForgejoError> { + let request = self + .patch(&format!("notifications/threads/{id}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 205 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a repository in an organization + /// + /// - `org`: name of organization + /// - `body`: See [`CreateRepoOption`] + pub async fn create_org_repo_deprecated( + &self, + org: &str, + body: CreateRepoOption, + ) -> Result<Repository, ForgejoError> { + let request = self.post(&format!("org/{org}/repos")).json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get list of organizations + /// + pub async fn org_get_all( + &self, + query: OrgGetAllQuery, + ) -> Result<Vec<Organization>, ForgejoError> { + let request = self.get(&format!("orgs?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create an organization + /// + /// - `organization`: See [`CreateOrgOption`] + pub async fn org_create( + &self, + organization: CreateOrgOption, + ) -> Result<Organization, ForgejoError> { + let request = self.post("orgs").json(&organization).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get an organization + /// + /// - `org`: name of the organization to get + pub async fn org_get(&self, org: &str) -> Result<Organization, ForgejoError> { + let request = self.get(&format!("orgs/{org}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete an organization + /// + /// - `org`: organization that is to be deleted + pub async fn org_delete(&self, org: &str) -> Result<(), ForgejoError> { + let request = self.delete(&format!("orgs/{org}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit an organization + /// + /// - `org`: name of the organization to edit + /// - `body`: See [`EditOrgOption`] + pub async fn org_edit( + &self, + org: &str, + body: EditOrgOption, + ) -> Result<Organization, ForgejoError> { + let request = self.patch(&format!("orgs/{org}")).json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's actions secrets + /// + /// - `org`: name of the organization + pub async fn org_list_actions_secrets( + &self, + org: &str, + query: OrgListActionsSecretsQuery, + ) -> Result<Vec<Secret>, ForgejoError> { + let request = self + .get(&format!("orgs/{org}/actions/secrets?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create or Update a secret value in an organization + /// + /// - `org`: name of organization + /// - `secretname`: name of the secret + /// - `body`: See [`CreateOrUpdateSecretOption`] + pub async fn update_org_secret( + &self, + org: &str, + secretname: &str, + body: CreateOrUpdateSecretOption, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!("orgs/{org}/actions/secrets/{secretname}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(()), + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a secret in an organization + /// + /// - `org`: name of organization + /// - `secretname`: name of the secret + pub async fn delete_org_secret(&self, org: &str, secretname: &str) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("orgs/{org}/actions/secrets/{secretname}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's activity feeds + /// + /// - `org`: name of the org + pub async fn org_list_activity_feeds( + &self, + org: &str, + query: OrgListActivityFeedsQuery, + ) -> Result<Vec<Activity>, ForgejoError> { + let request = self + .get(&format!("orgs/{org}/activities/feeds?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update Avatar + /// + /// - `org`: name of the organization + /// - `body`: See [`UpdateUserAvatarOption`] + pub async fn org_update_avatar( + &self, + org: &str, + body: UpdateUserAvatarOption, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!("orgs/{org}/avatar")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete Avatar + /// + /// - `org`: name of the organization + pub async fn org_delete_avatar(&self, org: &str) -> Result<(), ForgejoError> { + let request = self.delete(&format!("orgs/{org}/avatar")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Blocks a user from the organization + /// + /// - `org`: name of the org + /// - `username`: username of the user + pub async fn org_block_user(&self, org: &str, username: &str) -> Result<(), ForgejoError> { + let request = self.put(&format!("orgs/{org}/block/{username}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's webhooks + /// + /// - `org`: name of the organization + pub async fn org_list_hooks( + &self, + org: &str, + query: OrgListHooksQuery, + ) -> Result<Vec<Hook>, ForgejoError> { + let request = self.get(&format!("orgs/{org}/hooks?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a hook + /// + /// - `org`: name of the organization + /// - `body`: See [`CreateHookOption`] + pub async fn org_create_hook( + &self, + org: &str, + body: CreateHookOption, + ) -> Result<Hook, ForgejoError> { + let request = self + .post(&format!("orgs/{org}/hooks")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a hook + /// + /// - `org`: name of the organization + /// - `id`: id of the hook to get + pub async fn org_get_hook(&self, org: &str, id: u64) -> Result<Hook, ForgejoError> { + let request = self.get(&format!("orgs/{org}/hooks/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a hook + /// + /// - `org`: name of the organization + /// - `id`: id of the hook to delete + pub async fn org_delete_hook(&self, org: &str, id: u64) -> Result<(), ForgejoError> { + let request = self.delete(&format!("orgs/{org}/hooks/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a hook + /// + /// - `org`: name of the organization + /// - `id`: id of the hook to update + /// - `body`: See [`EditHookOption`] + pub async fn org_edit_hook( + &self, + org: &str, + id: u64, + body: EditHookOption, + ) -> Result<Hook, ForgejoError> { + let request = self + .patch(&format!("orgs/{org}/hooks/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's labels + /// + /// - `org`: name of the organization + pub async fn org_list_labels( + &self, + org: &str, + query: OrgListLabelsQuery, + ) -> Result<Vec<Label>, ForgejoError> { + let request = self.get(&format!("orgs/{org}/labels?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a label for an organization + /// + /// - `org`: name of the organization + /// - `body`: See [`CreateLabelOption`] + pub async fn org_create_label( + &self, + org: &str, + body: CreateLabelOption, + ) -> Result<Label, ForgejoError> { + let request = self + .post(&format!("orgs/{org}/labels")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a single label + /// + /// - `org`: name of the organization + /// - `id`: id of the label to get + pub async fn org_get_label(&self, org: &str, id: u64) -> Result<Label, ForgejoError> { + let request = self.get(&format!("orgs/{org}/labels/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a label + /// + /// - `org`: name of the organization + /// - `id`: id of the label to delete + pub async fn org_delete_label(&self, org: &str, id: u64) -> Result<(), ForgejoError> { + let request = self.delete(&format!("orgs/{org}/labels/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a label + /// + /// - `org`: name of the organization + /// - `id`: id of the label to edit + /// - `body`: See [`EditLabelOption`] + pub async fn org_edit_label( + &self, + org: &str, + id: u64, + body: EditLabelOption, + ) -> Result<Label, ForgejoError> { + let request = self + .patch(&format!("orgs/{org}/labels/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the organization's blocked users + /// + /// - `org`: name of the org + pub async fn org_list_blocked_users( + &self, + org: &str, + query: OrgListBlockedUsersQuery, + ) -> Result<Vec<BlockedUser>, ForgejoError> { + let request = self + .get(&format!("orgs/{org}/list_blocked?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's members + /// + /// - `org`: name of the organization + pub async fn org_list_members( + &self, + org: &str, + query: OrgListMembersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self.get(&format!("orgs/{org}/members?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if a user is a member of an organization + /// + /// - `org`: name of the organization + /// - `username`: username of the user + pub async fn org_is_member(&self, org: &str, username: &str) -> Result<(), ForgejoError> { + let request = self + .get(&format!("orgs/{org}/members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove a member from an organization + /// + /// - `org`: name of the organization + /// - `username`: username of the user + pub async fn org_delete_member(&self, org: &str, username: &str) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("orgs/{org}/members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's public members + /// + /// - `org`: name of the organization + pub async fn org_list_public_members( + &self, + org: &str, + query: OrgListPublicMembersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("orgs/{org}/public_members?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if a user is a public member of an organization + /// + /// - `org`: name of the organization + /// - `username`: username of the user + pub async fn org_is_public_member( + &self, + org: &str, + username: &str, + ) -> Result<(), ForgejoError> { + let request = self + .get(&format!("orgs/{org}/public_members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Publicize a user's membership + /// + /// - `org`: name of the organization + /// - `username`: username of the user + pub async fn org_publicize_member( + &self, + org: &str, + username: &str, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!("orgs/{org}/public_members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Conceal a user's membership + /// + /// - `org`: name of the organization + /// - `username`: username of the user + pub async fn org_conceal_member(&self, org: &str, username: &str) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("orgs/{org}/public_members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's repos + /// + /// - `org`: name of the organization + pub async fn org_list_repos( + &self, + org: &str, + query: OrgListReposQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self.get(&format!("orgs/{org}/repos?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a repository in an organization + /// + /// - `org`: name of organization + /// - `body`: See [`CreateRepoOption`] + pub async fn create_org_repo( + &self, + org: &str, + body: CreateRepoOption, + ) -> Result<Repository, ForgejoError> { + let request = self + .post(&format!("orgs/{org}/repos")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an organization's teams + /// + /// - `org`: name of the organization + pub async fn org_list_teams( + &self, + org: &str, + query: OrgListTeamsQuery, + ) -> Result<Vec<Team>, ForgejoError> { + let request = self.get(&format!("orgs/{org}/teams?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a team + /// + /// - `org`: name of the organization + /// - `body`: See [`CreateTeamOption`] + pub async fn org_create_team( + &self, + org: &str, + body: CreateTeamOption, + ) -> Result<Team, ForgejoError> { + let request = self + .post(&format!("orgs/{org}/teams")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Search for teams within an organization + /// + /// - `org`: name of the organization + pub async fn team_search( + &self, + org: &str, + query: TeamSearchQuery, + ) -> Result<TeamSearchResponse, ForgejoError> { + let request = self + .get(&format!("orgs/{org}/teams/search?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unblock a user from the organization + /// + /// - `org`: name of the org + /// - `username`: username of the user + pub async fn org_unblock_user(&self, org: &str, username: &str) -> Result<(), ForgejoError> { + let request = self + .put(&format!("orgs/{org}/unblock/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets all packages of an owner + /// + /// - `owner`: owner of the packages + pub async fn list_packages( + &self, + owner: &str, + query: ListPackagesQuery, + ) -> Result<Vec<Package>, ForgejoError> { + let request = self.get(&format!("packages/{owner}?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets a package + /// + /// - `owner`: owner of the package + /// - `type`: type of the package + /// - `name`: name of the package + /// - `version`: version of the package + pub async fn get_package( + &self, + owner: &str, + r#type: &str, + name: &str, + version: &str, + ) -> Result<Package, ForgejoError> { + let request = self + .get(&format!("packages/{owner}/{type}/{name}/{version}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a package + /// + /// - `owner`: owner of the package + /// - `type`: type of the package + /// - `name`: name of the package + /// - `version`: version of the package + pub async fn delete_package( + &self, + owner: &str, + r#type: &str, + name: &str, + version: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("packages/{owner}/{type}/{name}/{version}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets all files of a package + /// + /// - `owner`: owner of the package + /// - `type`: type of the package + /// - `name`: name of the package + /// - `version`: version of the package + pub async fn list_package_files( + &self, + owner: &str, + r#type: &str, + name: &str, + version: &str, + ) -> Result<Vec<PackageFile>, ForgejoError> { + let request = self + .get(&format!("packages/{owner}/{type}/{name}/{version}/files")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Search for issues across the repositories that the user has access to + /// + pub async fn issue_search_issues( + &self, + query: IssueSearchIssuesQuery, + ) -> Result<Vec<Issue>, ForgejoError> { + let request = self.get(&format!("repos/issues/search?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Migrate a remote git repository + /// + /// - `body`: See [`MigrateRepoOptions`] + pub async fn repo_migrate(&self, body: MigrateRepoOptions) -> Result<Repository, ForgejoError> { + let request = self.post("repos/migrate").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Search for repositories + /// + pub async fn repo_search(&self, query: RepoSearchQuery) -> Result<SearchResults, ForgejoError> { + let request = self.get(&format!("repos/search?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get(&self, owner: &str, repo: &str) -> Result<Repository, ForgejoError> { + let request = self.get(&format!("repos/{owner}/{repo}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a repository + /// + /// - `owner`: owner of the repo to delete + /// - `repo`: name of the repo to delete + pub async fn repo_delete(&self, owner: &str, repo: &str) -> Result<(), ForgejoError> { + let request = self.delete(&format!("repos/{owner}/{repo}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a repository's properties. Only fields that are set will be changed. + /// + /// - `owner`: owner of the repo to edit + /// - `repo`: name of the repo to edit + /// - `body`: Properties of a repo that you can edit + + /// See [`EditRepoOption`] + pub async fn repo_edit( + &self, + owner: &str, + repo: &str, + body: EditRepoOption, + ) -> Result<Repository, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create or Update a secret value in a repository + /// + /// - `owner`: owner of the repository + /// - `repo`: name of the repository + /// - `secretname`: name of the secret + /// - `body`: See [`CreateOrUpdateSecretOption`] + pub async fn update_repo_secret( + &self, + owner: &str, + repo: &str, + secretname: &str, + body: CreateOrUpdateSecretOption, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!( + "repos/{owner}/{repo}/actions/secrets/{secretname}" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(()), + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a secret in a repository + /// + /// - `owner`: owner of the repository + /// - `repo`: name of the repository + /// - `secretname`: name of the secret + pub async fn delete_repo_secret( + &self, + owner: &str, + repo: &str, + secretname: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/actions/secrets/{secretname}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's activity feeds + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_activity_feeds( + &self, + owner: &str, + repo: &str, + query: RepoListActivityFeedsQuery, + ) -> Result<Vec<Activity>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/activities/feeds?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get an archive of a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `archive`: the git reference for download with attached archive format (e.g. master.zip) + pub async fn repo_get_archive( + &self, + owner: &str, + repo: &str, + archive: &str, + ) -> 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(response.bytes().await?[..].to_vec()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Return all users that have write access and can be assigned to issues + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_assignees( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/assignees")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update avatar + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`UpdateRepoAvatarOption`] + pub async fn repo_update_avatar( + &self, + owner: &str, + repo: &str, + body: UpdateRepoAvatarOption, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/avatar")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete avatar + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_delete_avatar(&self, owner: &str, repo: &str) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/avatar")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List branch protections for a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_branch_protection( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<BranchProtection>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/branch_protections")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a branch protections for a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateBranchProtectionOption`] + pub async fn repo_create_branch_protection( + &self, + owner: &str, + repo: &str, + body: CreateBranchProtectionOption, + ) -> Result<BranchProtection, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/branch_protections")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a specific branch protection for the repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `name`: name of protected branch + pub async fn repo_get_branch_protection( + &self, + owner: &str, + repo: &str, + name: &str, + ) -> Result<BranchProtection, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/branch_protections/{name}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a specific branch protection for the repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `name`: name of protected branch + pub async fn repo_delete_branch_protection( + &self, + owner: &str, + repo: &str, + name: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/branch_protections/{name}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a branch protections for a repository. Only fields that are set will be changed + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `name`: name of protected branch + /// - `body`: See [`EditBranchProtectionOption`] + pub async fn repo_edit_branch_protection( + &self, + owner: &str, + repo: &str, + name: &str, + body: EditBranchProtectionOption, + ) -> Result<BranchProtection, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/branch_protections/{name}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's branches + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_branches( + &self, + owner: &str, + repo: &str, + query: RepoListBranchesQuery, + ) -> Result<Vec<Branch>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/branches?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a branch + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateBranchRepoOption`] + pub async fn repo_create_branch( + &self, + owner: &str, + repo: &str, + body: CreateBranchRepoOption, + ) -> Result<Branch, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/branches")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Retrieve a specific branch from a repository, including its effective branch protection + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `branch`: branch to get + pub async fn repo_get_branch( + &self, + owner: &str, + repo: &str, + branch: &str, + ) -> Result<Branch, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/branches/{branch}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a specific branch from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `branch`: branch to delete + pub async fn repo_delete_branch( + &self, + owner: &str, + repo: &str, + branch: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/branches/{branch}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's collaborators + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_collaborators( + &self, + owner: &str, + repo: &str, + query: RepoListCollaboratorsQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/collaborators?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if a user is a collaborator of a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `collaborator`: username of the collaborator + pub async fn repo_check_collaborator( + &self, + owner: &str, + repo: &str, + collaborator: &str, + ) -> Result<(), ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/collaborators/{collaborator}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a collaborator to a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `collaborator`: username of the collaborator to add + /// - `body`: See [`AddCollaboratorOption`] + pub async fn repo_add_collaborator( + &self, + owner: &str, + repo: &str, + collaborator: &str, + body: AddCollaboratorOption, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!( + "repos/{owner}/{repo}/collaborators/{collaborator}" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a collaborator from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `collaborator`: username of the collaborator to delete + pub async fn repo_delete_collaborator( + &self, + owner: &str, + repo: &str, + collaborator: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/collaborators/{collaborator}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get repository permissions for a user + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `collaborator`: username of the collaborator + pub async fn repo_get_repo_permissions( + &self, + owner: &str, + repo: &str, + collaborator: &str, + ) -> Result<RepoCollaboratorPermission, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/collaborators/{collaborator}/permission" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a list of all commits from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_all_commits( + &self, + owner: &str, + repo: &str, + query: RepoGetAllCommitsQuery, + ) -> 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().try_into()?, response.json().await?)), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a commit's combined status, by branch/tag/commit reference + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `ref`: name of branch/tag/commit + pub async fn repo_get_combined_status_by_ref( + &self, + owner: &str, + repo: &str, + r#ref: &str, + query: RepoGetCombinedStatusByRefQuery, + ) -> Result<CombinedStatus, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/commits/{ref}/status?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a commit's statuses, by branch/tag/commit reference + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `ref`: name of branch/tag/commit + pub async fn repo_list_statuses_by_ref( + &self, + owner: &str, + repo: &str, + r#ref: &str, + query: RepoListStatusesByRefQuery, + ) -> Result<Vec<CommitStatus>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/commits/{ref}/statuses?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets the metadata of all the entries of the root dir + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_contents_list( + &self, + owner: &str, + repo: &str, + query: RepoGetContentsListQuery, + ) -> Result<Vec<ContentsResponse>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/contents?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Modify multiple files in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`ChangeFilesOptions`] + pub async fn repo_change_files( + &self, + owner: &str, + repo: &str, + body: ChangeFilesOptions, + ) -> Result<FilesResponse, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/contents")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `filepath`: path of the dir, file, symlink or submodule in the repo + pub async fn repo_get_contents( + &self, + owner: &str, + repo: &str, + filepath: &str, + query: RepoGetContentsQuery, + ) -> Result<ContentsResponse, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/contents/{filepath}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a file in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `filepath`: path of the file to update + /// - `body`: See [`UpdateFileOptions`] + pub async fn repo_update_file( + &self, + owner: &str, + repo: &str, + filepath: &str, + body: UpdateFileOptions, + ) -> Result<FileResponse, ForgejoError> { + let request = self + .put(&format!("repos/{owner}/{repo}/contents/{filepath}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a file in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `filepath`: path of the file to create + /// - `body`: See [`CreateFileOptions`] + pub async fn repo_create_file( + &self, + owner: &str, + repo: &str, + filepath: &str, + body: CreateFileOptions, + ) -> Result<FileResponse, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/contents/{filepath}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a file in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `filepath`: path of the file to delete + /// - `body`: See [`DeleteFileOptions`] + pub async fn repo_delete_file( + &self, + owner: &str, + repo: &str, + filepath: &str, + body: DeleteFileOptions, + ) -> Result<FileDeleteResponse, ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/contents/{filepath}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Apply diff patch to repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`UpdateFileOptions`] + pub async fn repo_apply_diff_patch( + &self, + owner: &str, + repo: &str, + body: UpdateFileOptions, + ) -> Result<FileResponse, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/diffpatch")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get the EditorConfig definitions of a file in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `filepath`: filepath of file to get + pub async fn repo_get_editor_config( + &self, + owner: &str, + repo: &str, + filepath: &str, + query: RepoGetEditorConfigQuery, + ) -> Result<(), ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/editorconfig/{filepath}?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's forks + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn list_forks( + &self, + owner: &str, + repo: &str, + query: ListForksQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/forks?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Fork a repository + /// + /// - `owner`: owner of the repo to fork + /// - `repo`: name of the repo to fork + /// - `body`: See [`CreateForkOption`] + pub async fn create_fork( + &self, + owner: &str, + repo: &str, + body: CreateForkOption, + ) -> Result<Repository, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/forks")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 202 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets the blob of a repository. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: sha of the commit + pub async fn get_blob( + &self, + owner: &str, + repo: &str, + sha: &str, + ) -> Result<GitBlobResponse, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/git/blobs/{sha}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a single commit from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: a git ref or commit sha + pub async fn repo_get_single_commit( + &self, + owner: &str, + repo: &str, + sha: &str, + query: RepoGetSingleCommitQuery, + ) -> Result<Commit, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/git/commits/{sha}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a commit's diff or patch + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: SHA of the commit to get + /// - `diffType`: whether the output is diff or patch + pub async fn repo_download_commit_diff_or_patch( + &self, + owner: &str, + repo: &str, + sha: &str, + diff_type: &str, + ) -> Result<String, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/git/commits/{sha}.{diff_type}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a note corresponding to a single commit from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: a git ref or commit sha + pub async fn repo_get_note( + &self, + owner: &str, + repo: &str, + sha: &str, + query: RepoGetNoteQuery, + ) -> Result<Note, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/git/notes/{sha}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get specified ref or filtered repository's refs + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_all_git_refs( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<Reference>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/git/refs")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get specified ref or filtered repository's refs + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `ref`: part or full name of the ref + pub async fn repo_list_git_refs( + &self, + owner: &str, + repo: &str, + r#ref: &str, + ) -> Result<Vec<Reference>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/git/refs/{ref}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets the tag object of an annotated tag (not lightweight tags) + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: sha of the tag. The Git tags API only supports annotated tag objects, not lightweight tags. + pub async fn get_annotated_tag( + &self, + owner: &str, + repo: &str, + sha: &str, + ) -> Result<AnnotatedTag, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/git/tags/{sha}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets the tree of a repository. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: sha of the commit + pub async fn get_tree( + &self, + owner: &str, + repo: &str, + sha: &str, + query: GetTreeQuery, + ) -> Result<GitTreeResponse, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/git/trees/{sha}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the hooks in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_hooks( + &self, + owner: &str, + repo: &str, + query: RepoListHooksQuery, + ) -> Result<Vec<Hook>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/hooks?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a hook + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateHookOption`] + pub async fn repo_create_hook( + &self, + owner: &str, + repo: &str, + body: CreateHookOption, + ) -> Result<Hook, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/hooks")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the Git hooks in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_git_hooks( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<GitHook>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/hooks/git")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a Git hook + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the hook to get + pub async fn repo_get_git_hook( + &self, + owner: &str, + repo: &str, + id: &str, + ) -> Result<GitHook, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/hooks/git/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a Git hook in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the hook to get + pub async fn repo_delete_git_hook( + &self, + owner: &str, + repo: &str, + id: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/hooks/git/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a Git hook in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the hook to get + /// - `body`: See [`EditGitHookOption`] + pub async fn repo_edit_git_hook( + &self, + owner: &str, + repo: &str, + id: &str, + body: EditGitHookOption, + ) -> Result<GitHook, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/hooks/git/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a hook + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the hook to get + pub async fn repo_get_hook( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<Hook, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/hooks/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a hook in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the hook to delete + pub async fn repo_delete_hook( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/hooks/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a hook in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: index of the hook + /// - `body`: See [`EditHookOption`] + pub async fn repo_edit_hook( + &self, + owner: &str, + repo: &str, + id: u64, + body: EditHookOption, + ) -> Result<Hook, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/hooks/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Test a push webhook + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the hook to test + pub async fn repo_test_hook( + &self, + owner: &str, + repo: &str, + id: u64, + query: RepoTestHookQuery, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/hooks/{id}/tests?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns the issue config for a repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_issue_config( + &self, + owner: &str, + repo: &str, + ) -> Result<IssueConfig, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issue_config")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns the validation information for a issue config + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_validate_issue_config( + &self, + owner: &str, + repo: &str, + ) -> Result<IssueConfigValidation, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issue_config/validate")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get available issue templates for a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_issue_templates( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<IssueTemplate>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issue_templates")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's issues + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn issue_list_issues( + &self, + owner: &str, + repo: &str, + query: IssueListIssuesQuery, + ) -> Result<Vec<Issue>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create an issue. If using deadline only the date will be taken into account, and time of day ignored. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateIssueOption`] + pub async fn issue_create_issue( + &self, + owner: &str, + repo: &str, + body: CreateIssueOption, + ) -> Result<Issue, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List all comments in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn issue_get_repo_comments( + &self, + owner: &str, + repo: &str, + query: IssueGetRepoCommentsQuery, + ) -> Result<Vec<Comment>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues/comments?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a comment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment + pub async fn issue_get_comment( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<Option<Comment>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues/comments/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(Some(response.json().await?)), + 204 => Ok(Some(response.json().await?)), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a comment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of comment to delete + pub async fn issue_delete_comment( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/comments/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a comment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment to edit + /// - `body`: See [`EditIssueCommentOption`] + pub async fn issue_edit_comment( + &self, + owner: &str, + repo: &str, + id: u64, + body: EditIssueCommentOption, + ) -> Result<Option<Comment>, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/issues/comments/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(Some(response.json().await?)), + 204 => Ok(Some(response.json().await?)), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List comment's attachments + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment + pub async fn issue_list_issue_comment_attachments( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<Vec<Attachment>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues/comments/{id}/assets")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a comment attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment + /// - `attachment`: attachment to upload + pub async fn issue_create_issue_comment_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment: Vec<u8>, + query: IssueCreateIssueCommentAttachmentQuery, + ) -> Result<Attachment, ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/issues/comments/{id}/assets?{query}" + )) + .multipart( + reqwest::multipart::Form::new().part( + "attachment", + reqwest::multipart::Part::bytes(attachment) + .file_name("file") + .mime_str("*/*") + .unwrap(), + ), + ) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a comment attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment + /// - `attachment_id`: id of the attachment to get + pub async fn issue_get_issue_comment_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment_id: u64, + ) -> Result<Attachment, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a comment attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment + /// - `attachment_id`: id of the attachment to delete + pub async fn issue_delete_issue_comment_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment_id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a comment attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment + /// - `attachment_id`: id of the attachment to edit + /// - `body`: See [`EditAttachmentOptions`] + pub async fn issue_edit_issue_comment_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment_id: u64, + body: EditAttachmentOptions, + ) -> Result<Attachment, ForgejoError> { + let request = self + .patch(&format!( + "repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id}" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a list of reactions from a comment of an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment to edit + pub async fn issue_get_comment_reactions( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<Vec<Reaction>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/comments/{id}/reactions" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a reaction to a comment of an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment to edit + /// - `content`: See [`EditReactionOption`] + pub async fn issue_post_comment_reaction( + &self, + owner: &str, + repo: &str, + id: u64, + content: EditReactionOption, + ) -> Result<Reaction, ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/issues/comments/{id}/reactions" + )) + .json(&content) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove a reaction from a comment of an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the comment to edit + /// - `content`: See [`EditReactionOption`] + pub async fn issue_delete_comment_reaction( + &self, + owner: &str, + repo: &str, + id: u64, + content: EditReactionOption, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/issues/comments/{id}/reactions" + )) + .json(&content) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repo's pinned issues + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_pinned_issues( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<Issue>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues/pinned")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue to get + pub async fn issue_get_issue( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<Issue, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues/{index}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of issue to delete + pub async fn issue_delete( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit an issue. If using deadline only the date will be taken into account, and time of day ignored. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue to edit + /// - `body`: See [`EditIssueOption`] + pub async fn issue_edit_issue( + &self, + owner: &str, + repo: &str, + index: u64, + body: EditIssueOption, + ) -> Result<Issue, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/issues/{index}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List issue's attachments + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_list_issue_attachments( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<Vec<Attachment>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues/{index}/assets")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create an issue attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `attachment`: attachment to upload + pub async fn issue_create_issue_attachment( + &self, + owner: &str, + repo: &str, + index: u64, + attachment: Vec<u8>, + query: IssueCreateIssueAttachmentQuery, + ) -> Result<Attachment, ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/issues/{index}/assets?{query}" + )) + .multipart( + reqwest::multipart::Form::new().part( + "attachment", + reqwest::multipart::Part::bytes(attachment) + .file_name("file") + .mime_str("*/*") + .unwrap(), + ), + ) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get an issue attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `attachment_id`: id of the attachment to get + pub async fn issue_get_issue_attachment( + &self, + owner: &str, + repo: &str, + index: u64, + attachment_id: u64, + ) -> Result<Attachment, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/assets/{attachment_id}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete an issue attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `attachment_id`: id of the attachment to delete + pub async fn issue_delete_issue_attachment( + &self, + owner: &str, + repo: &str, + index: u64, + attachment_id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/issues/{index}/assets/{attachment_id}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit an issue attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `attachment_id`: id of the attachment to edit + /// - `body`: See [`EditAttachmentOptions`] + pub async fn issue_edit_issue_attachment( + &self, + owner: &str, + repo: &str, + index: u64, + attachment_id: u64, + body: EditAttachmentOptions, + ) -> Result<Attachment, ForgejoError> { + let request = self + .patch(&format!( + "repos/{owner}/{repo}/issues/{index}/assets/{attachment_id}" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List issues that are blocked by this issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_list_blocks( + &self, + owner: &str, + repo: &str, + index: &str, + query: IssueListBlocksQuery, + ) -> Result<Vec<Issue>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/blocks?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Block the issue given in the body by the issue in path + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`IssueMeta`] + pub async fn issue_create_issue_blocking( + &self, + owner: &str, + repo: &str, + index: &str, + body: IssueMeta, + ) -> Result<Issue, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/blocks")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unblock the issue given in the body by the issue in path + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`IssueMeta`] + pub async fn issue_remove_issue_blocking( + &self, + owner: &str, + repo: &str, + index: &str, + body: IssueMeta, + ) -> Result<Issue, ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/blocks")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List all comments on an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_get_comments( + &self, + owner: &str, + repo: &str, + index: u64, + query: IssueGetCommentsQuery, + ) -> Result<Vec<Comment>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/comments?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a comment to an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`CreateIssueCommentOption`] + pub async fn issue_create_comment( + &self, + owner: &str, + repo: &str, + index: u64, + body: CreateIssueCommentOption, + ) -> Result<Comment, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/comments")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a comment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: this parameter is ignored + /// - `id`: id of comment to delete + pub async fn issue_delete_comment_deprecated( + &self, + owner: &str, + repo: &str, + index: u32, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/issues/{index}/comments/{id}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a comment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: this parameter is ignored + /// - `id`: id of the comment to edit + /// - `body`: See [`EditIssueCommentOption`] + pub async fn issue_edit_comment_deprecated( + &self, + owner: &str, + repo: &str, + index: u32, + id: u64, + body: EditIssueCommentOption, + ) -> Result<Option<Comment>, ForgejoError> { + let request = self + .patch(&format!( + "repos/{owner}/{repo}/issues/{index}/comments/{id}" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(Some(response.json().await?)), + 204 => Ok(Some(response.json().await?)), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Set an issue deadline. If set to null, the deadline is deleted. If using deadline only the date will be taken into account, and time of day ignored. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue to create or update a deadline on + /// - `body`: See [`EditDeadlineOption`] + pub async fn issue_edit_issue_deadline( + &self, + owner: &str, + repo: &str, + index: u64, + body: EditDeadlineOption, + ) -> Result<IssueDeadline, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/deadline")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an issue's dependencies, i.e all issues that block this issue. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_list_issue_dependencies( + &self, + owner: &str, + repo: &str, + index: &str, + query: IssueListIssueDependenciesQuery, + ) -> Result<Vec<Issue>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/dependencies?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Make the issue in the url depend on the issue in the form. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`IssueMeta`] + pub async fn issue_create_issue_dependencies( + &self, + owner: &str, + repo: &str, + index: &str, + body: IssueMeta, + ) -> Result<Issue, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/dependencies")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove an issue dependency + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`IssueMeta`] + pub async fn issue_remove_issue_dependencies( + &self, + owner: &str, + repo: &str, + index: &str, + body: IssueMeta, + ) -> Result<Issue, ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/dependencies")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get an issue's labels + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_get_labels( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<Vec<Label>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/issues/{index}/labels")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Replace an issue's labels + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`IssueLabelsOption`] + pub async fn issue_replace_labels( + &self, + owner: &str, + repo: &str, + index: u64, + body: IssueLabelsOption, + ) -> Result<Vec<Label>, ForgejoError> { + let request = self + .put(&format!("repos/{owner}/{repo}/issues/{index}/labels")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a label to an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`IssueLabelsOption`] + pub async fn issue_add_label( + &self, + owner: &str, + repo: &str, + index: u64, + body: IssueLabelsOption, + ) -> Result<Vec<Label>, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/labels")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove all labels from an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`DeleteLabelsOption`] + pub async fn issue_clear_labels( + &self, + owner: &str, + repo: &str, + index: u64, + body: DeleteLabelsOption, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/labels")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove a label from an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `id`: id of the label to remove + /// - `body`: See [`DeleteLabelsOption`] + pub async fn issue_remove_label( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + body: DeleteLabelsOption, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/labels/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Pin an Issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of issue to pin + pub async fn pin_issue(&self, owner: &str, repo: &str, index: u64) -> Result<(), ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/pin")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unpin an Issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of issue to unpin + pub async fn unpin_issue( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/pin")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Moves the Pin to the given Position + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of issue + /// - `position`: the new position + pub async fn move_issue_pin( + &self, + owner: &str, + repo: &str, + index: u64, + position: u64, + ) -> Result<(), ForgejoError> { + let request = self + .patch(&format!( + "repos/{owner}/{repo}/issues/{index}/pin/{position}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a list reactions of an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_get_issue_reactions( + &self, + owner: &str, + repo: &str, + index: u64, + query: IssueGetIssueReactionsQuery, + ) -> Result<Vec<Reaction>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/reactions?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a reaction to an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `content`: See [`EditReactionOption`] + pub async fn issue_post_issue_reaction( + &self, + owner: &str, + repo: &str, + index: u64, + content: EditReactionOption, + ) -> Result<Reaction, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/reactions")) + .json(&content) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove a reaction from an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `content`: See [`EditReactionOption`] + pub async fn issue_delete_issue_reaction( + &self, + owner: &str, + repo: &str, + index: u64, + content: EditReactionOption, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/reactions")) + .json(&content) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete an issue's existing stopwatch. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue to stop the stopwatch on + pub async fn issue_delete_stop_watch( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/issues/{index}/stopwatch/delete" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Start stopwatch on an issue. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue to create the stopwatch on + pub async fn issue_start_stop_watch( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/issues/{index}/stopwatch/start" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Stop an issue's existing stopwatch. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue to stop the stopwatch on + pub async fn issue_stop_stop_watch( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/issues/{index}/stopwatch/stop" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get users who subscribed on an issue. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_subscriptions( + &self, + owner: &str, + repo: &str, + index: u64, + query: IssueSubscriptionsQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/subscriptions?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if user is subscribed to an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_check_subscription( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<WatchInfo, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/subscriptions/check" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Subscribe user to issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `user`: user to subscribe + pub async fn issue_add_subscription( + &self, + owner: &str, + repo: &str, + index: u64, + user: &str, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!( + "repos/{owner}/{repo}/issues/{index}/subscriptions/{user}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + 201 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unsubscribe user from issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `user`: user witch unsubscribe + pub async fn issue_delete_subscription( + &self, + owner: &str, + repo: &str, + index: u64, + user: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/issues/{index}/subscriptions/{user}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + 201 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List all comments and events on an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_get_comments_and_timeline( + &self, + owner: &str, + repo: &str, + index: u64, + query: IssueGetCommentsAndTimelineQuery, + ) -> Result<Vec<TimelineComment>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/timeline?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List an issue's tracked times + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + pub async fn issue_tracked_times( + &self, + owner: &str, + repo: &str, + index: u64, + query: IssueTrackedTimesQuery, + ) -> Result<Vec<TrackedTime>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/issues/{index}/times?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add tracked time to a issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `body`: See [`AddTimeOption`] + pub async fn issue_add_time( + &self, + owner: &str, + repo: &str, + index: u64, + body: AddTimeOption, + ) -> Result<TrackedTime, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/issues/{index}/times")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Reset a tracked time of an issue + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue to add tracked time to + pub async fn issue_reset_time( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/times")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete specific tracked time + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the issue + /// - `id`: id of time to delete + pub async fn issue_delete_time( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/issues/{index}/times/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's keys + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_keys( + &self, + owner: &str, + repo: &str, + query: RepoListKeysQuery, + ) -> Result<Vec<DeployKey>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/keys?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a key to a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateKeyOption`] + pub async fn repo_create_key( + &self, + owner: &str, + repo: &str, + body: CreateKeyOption, + ) -> Result<DeployKey, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/keys")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a repository's key by id + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the key to get + pub async fn repo_get_key( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<DeployKey, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/keys/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a key from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the key to delete + pub async fn repo_delete_key( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/keys/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get all of a repository's labels + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn issue_list_labels( + &self, + owner: &str, + repo: &str, + query: IssueListLabelsQuery, + ) -> Result<Vec<Label>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/labels?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a label + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateLabelOption`] + pub async fn issue_create_label( + &self, + owner: &str, + repo: &str, + body: CreateLabelOption, + ) -> Result<Label, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/labels")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a single label + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the label to get + pub async fn issue_get_label( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<Label, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/labels/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a label + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the label to delete + pub async fn issue_delete_label( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/labels/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a label + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the label to edit + /// - `body`: See [`EditLabelOption`] + pub async fn issue_edit_label( + &self, + owner: &str, + repo: &str, + id: u64, + body: EditLabelOption, + ) -> Result<Label, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/labels/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get languages and number of bytes of code written + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_languages( + &self, + owner: &str, + repo: &str, + ) -> Result<BTreeMap<String, u64>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/languages")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a file or it's LFS object from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `filepath`: filepath of the file to get + pub async fn repo_get_raw_file_or_lfs( + &self, + owner: &str, + repo: &str, + filepath: &str, + query: RepoGetRawFileOrLfsQuery, + ) -> Result<(), ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/media/{filepath}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get all of a repository's opened milestones + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn issue_get_milestones_list( + &self, + owner: &str, + repo: &str, + query: IssueGetMilestonesListQuery, + ) -> Result<Vec<Milestone>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/milestones?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a milestone + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateMilestoneOption`] + pub async fn issue_create_milestone( + &self, + owner: &str, + repo: &str, + body: CreateMilestoneOption, + ) -> Result<Milestone, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/milestones")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a milestone + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: the milestone to get, identified by ID and if not available by name + pub async fn issue_get_milestone( + &self, + owner: &str, + repo: &str, + id: &str, + ) -> Result<Milestone, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/milestones/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a milestone + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: the milestone to delete, identified by ID and if not available by name + pub async fn issue_delete_milestone( + &self, + owner: &str, + repo: &str, + id: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/milestones/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a milestone + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: the milestone to edit, identified by ID and if not available by name + /// - `body`: See [`EditMilestoneOption`] + pub async fn issue_edit_milestone( + &self, + owner: &str, + repo: &str, + id: &str, + body: EditMilestoneOption, + ) -> Result<Milestone, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/milestones/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Sync a mirrored repository + /// + /// - `owner`: owner of the repo to sync + /// - `repo`: name of the repo to sync + pub async fn repo_mirror_sync(&self, owner: &str, repo: &str) -> Result<(), ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/mirror-sync")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns if new Issue Pins are allowed + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_new_pin_allowed( + &self, + owner: &str, + repo: &str, + ) -> Result<NewIssuePinsAllowed, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/new_pin_allowed")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List users's notification threads on a specific repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn notify_get_repo_list( + &self, + owner: &str, + repo: &str, + query: NotifyGetRepoListQuery, + ) -> Result<Vec<NotificationThread>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/notifications?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Mark notification threads as read, pinned or unread on a specific repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn notify_read_repo_list( + &self, + owner: &str, + repo: &str, + query: NotifyReadRepoListQuery, + ) -> Result<Vec<NotificationThread>, ForgejoError> { + let request = self + .put(&format!("repos/{owner}/{repo}/notifications?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 205 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repo's pull requests + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_pull_requests( + &self, + owner: &str, + repo: &str, + query: RepoListPullRequestsQuery, + ) -> Result<Vec<PullRequest>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/pulls?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreatePullRequestOption`] + pub async fn repo_create_pull_request( + &self, + owner: &str, + repo: &str, + body: CreatePullRequestOption, + ) -> Result<PullRequest, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/pulls")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repo's pinned pull requests + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_pinned_pull_requests( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<PullRequest>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/pulls/pinned")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to get + pub async fn repo_get_pull_request( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<PullRequest, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/pulls/{index}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a pull request. If using deadline only the date will be taken into account, and time of day ignored. + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to edit + /// - `body`: See [`EditPullRequestOption`] + pub async fn repo_edit_pull_request( + &self, + owner: &str, + repo: &str, + index: u64, + body: EditPullRequestOption, + ) -> Result<PullRequest, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/pulls/{index}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a pull request diff or patch + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to get + /// - `diffType`: whether the output is diff or patch + pub async fn repo_download_pull_diff_or_patch( + &self, + owner: &str, + repo: &str, + index: u64, + diff_type: &str, + query: RepoDownloadPullDiffOrPatchQuery, + ) -> Result<String, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/pulls/{index}.{diff_type}?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get commits for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to get + pub async fn repo_get_pull_request_commits( + &self, + owner: &str, + repo: &str, + index: u64, + query: RepoGetPullRequestCommitsQuery, + ) -> Result<(CommitListHeaders, Vec<Commit>), ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/pulls/{index}/commits?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok((response.headers().try_into()?, response.json().await?)), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get changed files for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to get + pub async fn repo_get_pull_request_files( + &self, + owner: &str, + repo: &str, + index: u64, + query: RepoGetPullRequestFilesQuery, + ) -> 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().try_into()?, response.json().await?)), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if a pull request has been merged + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + pub async fn repo_pull_request_is_merged( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/pulls/{index}/merge")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Merge a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to merge + /// - `body`: See [`MergePullRequestOption`] + pub async fn repo_merge_pull_request( + &self, + owner: &str, + repo: &str, + index: u64, + body: MergePullRequestOption, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/pulls/{index}/merge")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Cancel the scheduled auto merge for the given pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to merge + pub async fn repo_cancel_scheduled_auto_merge( + &self, + owner: &str, + repo: &str, + index: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/pulls/{index}/merge")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// create review requests for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `body`: See [`PullReviewRequestOptions`] + pub async fn repo_create_pull_review_requests( + &self, + owner: &str, + repo: &str, + index: u64, + body: PullReviewRequestOptions, + ) -> Result<Vec<PullReview>, ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/pulls/{index}/requested_reviewers" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// cancel review requests for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `body`: See [`PullReviewRequestOptions`] + pub async fn repo_delete_pull_review_requests( + &self, + owner: &str, + repo: &str, + index: u64, + body: PullReviewRequestOptions, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/pulls/{index}/requested_reviewers" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List all reviews for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + pub async fn repo_list_pull_reviews( + &self, + owner: &str, + repo: &str, + index: u64, + query: RepoListPullReviewsQuery, + ) -> Result<Vec<PullReview>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/pulls/{index}/reviews?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a review to an pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `body`: See [`CreatePullReviewOptions`] + pub async fn repo_create_pull_review( + &self, + owner: &str, + repo: &str, + index: u64, + body: CreatePullReviewOptions, + ) -> Result<PullReview, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/pulls/{index}/reviews")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a specific review for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `id`: id of the review + pub async fn repo_get_pull_review( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + ) -> Result<PullReview, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/pulls/{index}/reviews/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Submit a pending review to an pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `id`: id of the review + /// - `body`: See [`SubmitPullReviewOptions`] + pub async fn repo_submit_pull_review( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + body: SubmitPullReviewOptions, + ) -> Result<PullReview, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/pulls/{index}/reviews/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a specific review from a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `id`: id of the review + pub async fn repo_delete_pull_review( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/pulls/{index}/reviews/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a specific review for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `id`: id of the review + pub async fn repo_get_pull_review_comments( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + ) -> Result<Vec<PullReviewComment>, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Dismiss a review for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `id`: id of the review + /// - `body`: See [`DismissPullReviewOptions`] + pub async fn repo_dismiss_pull_review( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + body: DismissPullReviewOptions, + ) -> Result<PullReview, ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/pulls/{index}/reviews/{id}/dismissals" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Cancel to dismiss a review for a pull request + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request + /// - `id`: id of the review + pub async fn repo_un_dismiss_pull_review( + &self, + owner: &str, + repo: &str, + index: u64, + id: u64, + ) -> Result<PullReview, ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/pulls/{index}/reviews/{id}/undismissals" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Merge PR's baseBranch into headBranch + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `index`: index of the pull request to get + pub async fn repo_update_pull_request( + &self, + owner: &str, + repo: &str, + index: u64, + query: RepoUpdatePullRequestQuery, + ) -> Result<(), ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/pulls/{index}/update?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get all push mirrors of the repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_push_mirrors( + &self, + owner: &str, + repo: &str, + query: RepoListPushMirrorsQuery, + ) -> Result<Vec<PushMirror>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/push_mirrors?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// add a push mirror to the repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreatePushMirrorOption`] + pub async fn repo_add_push_mirror( + &self, + owner: &str, + repo: &str, + body: CreatePushMirrorOption, + ) -> Result<PushMirror, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/push_mirrors")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Sync all push mirrored repository + /// + /// - `owner`: owner of the repo to sync + /// - `repo`: name of the repo to sync + pub async fn repo_push_mirror_sync(&self, owner: &str, repo: &str) -> Result<(), ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/push_mirrors-sync")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get push mirror of the repository by remoteName + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `name`: remote name of push mirror + pub async fn repo_get_push_mirror_by_remote_name( + &self, + owner: &str, + repo: &str, + name: &str, + ) -> Result<PushMirror, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/push_mirrors/{name}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// deletes a push mirror from a repository by remoteName + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `name`: remote name of the pushMirror + pub async fn repo_delete_push_mirror( + &self, + owner: &str, + repo: &str, + name: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/push_mirrors/{name}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a file from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `filepath`: filepath of the file to get + pub async fn repo_get_raw_file( + &self, + owner: &str, + repo: &str, + filepath: &str, + query: RepoGetRawFileQuery, + ) -> Result<(), ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/raw/{filepath}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repo's releases + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_releases( + &self, + owner: &str, + repo: &str, + query: RepoListReleasesQuery, + ) -> Result<Vec<Release>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/releases?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a release + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateReleaseOption`] + pub async fn repo_create_release( + &self, + owner: &str, + repo: &str, + body: CreateReleaseOption, + ) -> Result<Release, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/releases")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_latest_release( + &self, + owner: &str, + repo: &str, + ) -> Result<Release, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/releases/latest")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a release by tag name + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `tag`: tag name of the release to get + pub async fn repo_get_release_by_tag( + &self, + owner: &str, + repo: &str, + tag: &str, + ) -> Result<Release, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/releases/tags/{tag}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a release by tag name + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `tag`: tag name of the release to delete + pub async fn repo_delete_release_by_tag( + &self, + owner: &str, + repo: &str, + tag: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/releases/tags/{tag}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a release + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release to get + pub async fn repo_get_release( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<Release, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/releases/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a release + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release to delete + pub async fn repo_delete_release( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/releases/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a release + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release to edit + /// - `body`: See [`EditReleaseOption`] + pub async fn repo_edit_release( + &self, + owner: &str, + repo: &str, + id: u64, + body: EditReleaseOption, + ) -> Result<Release, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/releases/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List release's attachments + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release + pub async fn repo_list_release_attachments( + &self, + owner: &str, + repo: &str, + id: u64, + ) -> Result<Vec<Attachment>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/releases/{id}/assets")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a release attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release + /// - `attachment`: attachment to upload + pub async fn repo_create_release_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment: Vec<u8>, + query: RepoCreateReleaseAttachmentQuery, + ) -> Result<Attachment, ForgejoError> { + let request = self + .post(&format!( + "repos/{owner}/{repo}/releases/{id}/assets?{query}" + )) + .multipart( + reqwest::multipart::Form::new().part( + "attachment", + reqwest::multipart::Part::bytes(attachment) + .file_name("file") + .mime_str("*/*") + .unwrap(), + ), + ) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a release attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release + /// - `attachment_id`: id of the attachment to get + pub async fn repo_get_release_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment_id: u64, + ) -> Result<Attachment, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a release attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release + /// - `attachment_id`: id of the attachment to delete + pub async fn repo_delete_release_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment_id: u64, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!( + "repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a release attachment + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `id`: id of the release + /// - `attachment_id`: id of the attachment to edit + /// - `body`: See [`EditAttachmentOptions`] + pub async fn repo_edit_release_attachment( + &self, + owner: &str, + repo: &str, + id: u64, + attachment_id: u64, + body: EditAttachmentOptions, + ) -> Result<Attachment, ForgejoError> { + let request = self + .patch(&format!( + "repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}" + )) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Return all users that can be requested to review in this repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_reviewers( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/reviewers")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get signing-key.gpg for given repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_signing_key(&self, owner: &str, repo: &str) -> Result<String, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/signing-key.gpg")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repo's stargazers + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_stargazers( + &self, + owner: &str, + repo: &str, + query: RepoListStargazersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/stargazers?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a commit's statuses + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: sha of the commit + pub async fn repo_list_statuses( + &self, + owner: &str, + repo: &str, + sha: &str, + query: RepoListStatusesQuery, + ) -> Result<Vec<CommitStatus>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/statuses/{sha}?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a commit status + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `sha`: sha of the commit + /// - `body`: See [`CreateStatusOption`] + pub async fn repo_create_status( + &self, + owner: &str, + repo: &str, + sha: &str, + body: CreateStatusOption, + ) -> Result<CommitStatus, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/statuses/{sha}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repo's watchers + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_subscribers( + &self, + owner: &str, + repo: &str, + query: RepoListSubscribersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/subscribers?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if the current user is watching a repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn user_current_check_subscription( + &self, + owner: &str, + repo: &str, + ) -> Result<WatchInfo, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/subscription")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Watch a repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn user_current_put_subscription( + &self, + owner: &str, + repo: &str, + ) -> Result<WatchInfo, ForgejoError> { + let request = self + .put(&format!("repos/{owner}/{repo}/subscription")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unwatch a repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn user_current_delete_subscription( + &self, + owner: &str, + repo: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/subscription")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's tags + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_tags( + &self, + owner: &str, + repo: &str, + query: RepoListTagsQuery, + ) -> Result<Vec<Tag>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/tags?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a new git tag in a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateTagOption`] + pub async fn repo_create_tag( + &self, + owner: &str, + repo: &str, + body: CreateTagOption, + ) -> Result<Tag, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/tags")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get the tag of a repository by tag name + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `tag`: name of tag + pub async fn repo_get_tag( + &self, + owner: &str, + repo: &str, + tag: &str, + ) -> Result<Tag, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/tags/{tag}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a repository's tag by name + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `tag`: name of tag to delete + pub async fn repo_delete_tag( + &self, + owner: &str, + repo: &str, + tag: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/tags/{tag}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repository's teams + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_teams( + &self, + owner: &str, + repo: &str, + ) -> Result<Vec<Team>, ForgejoError> { + let request = self.get(&format!("repos/{owner}/{repo}/teams")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if a team is assigned to a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `team`: team name + pub async fn repo_check_team( + &self, + owner: &str, + repo: &str, + team: &str, + ) -> Result<Team, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/teams/{team}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a team to a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `team`: team name + pub async fn repo_add_team( + &self, + owner: &str, + repo: &str, + team: &str, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!("repos/{owner}/{repo}/teams/{team}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a team from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `team`: team name + pub async fn repo_delete_team( + &self, + owner: &str, + repo: &str, + team: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/teams/{team}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a repo's tracked times + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_tracked_times( + &self, + owner: &str, + repo: &str, + query: RepoTrackedTimesQuery, + ) -> Result<Vec<TrackedTime>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/times?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a user's tracked times in a repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `user`: username of user + pub async fn user_tracked_times( + &self, + owner: &str, + repo: &str, + user: &str, + ) -> Result<Vec<TrackedTime>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/times/{user}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get list of topics that a repository has + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_list_topics( + &self, + owner: &str, + repo: &str, + query: RepoListTopicsQuery, + ) -> Result<TopicName, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/topics?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Replace list of topics for a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`RepoTopicOptions`] + pub async fn repo_update_topics( + &self, + owner: &str, + repo: &str, + body: RepoTopicOptions, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!("repos/{owner}/{repo}/topics")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a topic to a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `topic`: name of the topic to add + pub async fn repo_add_topic( + &self, + owner: &str, + repo: &str, + topic: &str, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!("repos/{owner}/{repo}/topics/{topic}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a topic from a repository + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `topic`: name of the topic to delete + pub async fn repo_delete_topic( + &self, + owner: &str, + repo: &str, + topic: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/topics/{topic}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Transfer a repo ownership + /// + /// - `owner`: owner of the repo to transfer + /// - `repo`: name of the repo to transfer + /// - `body`: Transfer Options + + /// See [`TransferRepoOption`] + pub async fn repo_transfer( + &self, + owner: &str, + repo: &str, + body: TransferRepoOption, + ) -> Result<Repository, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/transfer")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 202 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Accept a repo transfer + /// + /// - `owner`: owner of the repo to transfer + /// - `repo`: name of the repo to transfer + pub async fn accept_repo_transfer( + &self, + owner: &str, + repo: &str, + ) -> Result<Repository, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/transfer/accept")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 202 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Reject a repo transfer + /// + /// - `owner`: owner of the repo to transfer + /// - `repo`: name of the repo to transfer + pub async fn reject_repo_transfer( + &self, + owner: &str, + repo: &str, + ) -> Result<Repository, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/transfer/reject")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a wiki page + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `body`: See [`CreateWikiPageOptions`] + pub async fn repo_create_wiki_page( + &self, + owner: &str, + repo: &str, + body: CreateWikiPageOptions, + ) -> Result<WikiPage, ForgejoError> { + let request = self + .post(&format!("repos/{owner}/{repo}/wiki/new")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a wiki page + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `pageName`: name of the page + pub async fn repo_get_wiki_page( + &self, + owner: &str, + repo: &str, + page_name: &str, + ) -> Result<WikiPage, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/wiki/page/{page_name}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a wiki page + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `pageName`: name of the page + pub async fn repo_delete_wiki_page( + &self, + owner: &str, + repo: &str, + page_name: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("repos/{owner}/{repo}/wiki/page/{page_name}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a wiki page + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `pageName`: name of the page + /// - `body`: See [`CreateWikiPageOptions`] + pub async fn repo_edit_wiki_page( + &self, + owner: &str, + repo: &str, + page_name: &str, + body: CreateWikiPageOptions, + ) -> Result<WikiPage, ForgejoError> { + let request = self + .patch(&format!("repos/{owner}/{repo}/wiki/page/{page_name}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get all wiki pages + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn repo_get_wiki_pages( + &self, + owner: &str, + repo: &str, + query: RepoGetWikiPagesQuery, + ) -> Result<Vec<WikiPageMetaData>, ForgejoError> { + let request = self + .get(&format!("repos/{owner}/{repo}/wiki/pages?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get revisions of a wiki page + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + /// - `pageName`: name of the page + pub async fn repo_get_wiki_page_revisions( + &self, + owner: &str, + repo: &str, + page_name: &str, + query: RepoGetWikiPageRevisionsQuery, + ) -> Result<WikiCommitList, ForgejoError> { + let request = self + .get(&format!( + "repos/{owner}/{repo}/wiki/revisions/{page_name}?{query}" + )) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a repository using a template + /// + /// - `template_owner`: name of the template repository owner + /// - `template_repo`: name of the template repository + /// - `body`: See [`GenerateRepoOption`] + pub async fn generate_repo( + &self, + template_owner: &str, + template_repo: &str, + body: GenerateRepoOption, + ) -> Result<Repository, ForgejoError> { + let request = self + .post(&format!("repos/{template_owner}/{template_repo}/generate")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a repository by id + /// + /// - `id`: id of the repo to get + pub async fn repo_get_by_id(&self, id: u64) -> Result<Repository, ForgejoError> { + let request = self.get(&format!("repositories/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get instance's global settings for api + pub async fn get_general_api_settings(&self) -> Result<GeneralAPISettings, ForgejoError> { + let request = self.get("settings/api").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get instance's global settings for Attachment + pub async fn get_general_attachment_settings( + &self, + ) -> Result<GeneralAttachmentSettings, ForgejoError> { + let request = self.get("settings/attachment").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get instance's global settings for repositories + pub async fn get_general_repository_settings( + &self, + ) -> Result<GeneralRepoSettings, ForgejoError> { + let request = self.get("settings/repository").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get instance's global settings for ui + pub async fn get_general_ui_settings(&self) -> Result<GeneralUISettings, ForgejoError> { + let request = self.get("settings/ui").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get default signing-key.gpg + pub async fn get_signing_key(&self) -> Result<String, ForgejoError> { + let request = self.get("signing-key.gpg").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a team + /// + /// - `id`: id of the team to get + pub async fn org_get_team(&self, id: u64) -> Result<Team, ForgejoError> { + let request = self.get(&format!("teams/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a team + /// + /// - `id`: id of the team to delete + pub async fn org_delete_team(&self, id: u64) -> Result<(), ForgejoError> { + let request = self.delete(&format!("teams/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Edit a team + /// + /// - `id`: id of the team to edit + /// - `body`: See [`EditTeamOption`] + pub async fn org_edit_team(&self, id: u32, body: EditTeamOption) -> Result<Team, ForgejoError> { + let request = self.patch(&format!("teams/{id}")).json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a team's activity feeds + /// + /// - `id`: id of the team + pub async fn org_list_team_activity_feeds( + &self, + id: u64, + query: OrgListTeamActivityFeedsQuery, + ) -> Result<Vec<Activity>, ForgejoError> { + let request = self + .get(&format!("teams/{id}/activities/feeds?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a team's members + /// + /// - `id`: id of the team + pub async fn org_list_team_members( + &self, + id: u64, + query: OrgListTeamMembersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self.get(&format!("teams/{id}/members?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a particular member of team + /// + /// - `id`: id of the team + /// - `username`: username of the member to list + pub async fn org_list_team_member( + &self, + id: u64, + username: &str, + ) -> Result<User, ForgejoError> { + let request = self + .get(&format!("teams/{id}/members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a team member + /// + /// - `id`: id of the team + /// - `username`: username of the user to add + pub async fn org_add_team_member(&self, id: u64, username: &str) -> Result<(), ForgejoError> { + let request = self + .put(&format!("teams/{id}/members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove a team member + /// + /// - `id`: id of the team + /// - `username`: username of the user to remove + pub async fn org_remove_team_member( + &self, + id: u64, + username: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("teams/{id}/members/{username}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a team's repos + /// + /// - `id`: id of the team + pub async fn org_list_team_repos( + &self, + id: u64, + query: OrgListTeamReposQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self.get(&format!("teams/{id}/repos?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a particular repo of team + /// + /// - `id`: id of the team + /// - `org`: organization that owns the repo to list + /// - `repo`: name of the repo to list + pub async fn org_list_team_repo( + &self, + id: u64, + org: &str, + repo: &str, + ) -> Result<Repository, ForgejoError> { + let request = self + .get(&format!("teams/{id}/repos/{org}/{repo}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add a repository to a team + /// + /// - `id`: id of the team + /// - `org`: organization that owns the repo to add + /// - `repo`: name of the repo to add + pub async fn org_add_team_repository( + &self, + id: u64, + org: &str, + repo: &str, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!("teams/{id}/repos/{org}/{repo}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove a repository from a team + /// + /// - `id`: id of the team + /// - `org`: organization that owns the repo to remove + /// - `repo`: name of the repo to remove + pub async fn org_remove_team_repository( + &self, + id: u64, + org: &str, + repo: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("teams/{id}/repos/{org}/{repo}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// search topics via keyword + /// + pub async fn topic_search( + &self, + query: TopicSearchQuery, + ) -> Result<Vec<TopicResponse>, ForgejoError> { + let request = self.get(&format!("topics/search?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get the authenticated user + pub async fn user_get_current(&self) -> Result<User, ForgejoError> { + let request = self.get("user").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create or Update a secret value in a user scope + /// + /// - `secretname`: name of the secret + /// - `body`: See [`CreateOrUpdateSecretOption`] + pub async fn update_user_secret( + &self, + secretname: &str, + body: CreateOrUpdateSecretOption, + ) -> Result<(), ForgejoError> { + let request = self + .put(&format!("user/actions/secrets/{secretname}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(()), + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a secret in a user scope + /// + /// - `secretname`: name of the secret + pub async fn delete_user_secret(&self, secretname: &str) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("user/actions/secrets/{secretname}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's oauth2 applications + /// + pub async fn user_get_oauth2_applications( + &self, + query: UserGetOAuth2ApplicationsQuery, + ) -> Result<Vec<OAuth2Application>, ForgejoError> { + let request = self + .get(&format!("user/applications/oauth2?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// creates a new OAuth2 application + /// + /// - `body`: See [`CreateOAuth2ApplicationOptions`] + pub async fn user_create_oauth2_application( + &self, + body: CreateOAuth2ApplicationOptions, + ) -> Result<OAuth2Application, ForgejoError> { + let request = self.post("user/applications/oauth2").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// get an OAuth2 Application + /// + /// - `id`: Application ID to be found + pub async fn user_get_oauth2_application( + &self, + id: u64, + ) -> Result<OAuth2Application, ForgejoError> { + let request = self + .get(&format!("user/applications/oauth2/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// delete an OAuth2 Application + /// + /// - `id`: token to be deleted + pub async fn user_delete_oauth2_application(&self, id: u64) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("user/applications/oauth2/{id}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// update an OAuth2 Application, this includes regenerating the client secret + /// + /// - `id`: application to be updated + /// - `body`: See [`CreateOAuth2ApplicationOptions`] + pub async fn user_update_oauth2_application( + &self, + id: u64, + body: CreateOAuth2ApplicationOptions, + ) -> Result<OAuth2Application, ForgejoError> { + let request = self + .patch(&format!("user/applications/oauth2/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update Avatar + /// + /// - `body`: See [`UpdateUserAvatarOption`] + pub async fn user_update_avatar( + &self, + body: UpdateUserAvatarOption, + ) -> Result<(), ForgejoError> { + let request = self.post("user/avatar").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete Avatar + pub async fn user_delete_avatar(&self) -> Result<(), ForgejoError> { + let request = self.delete("user/avatar").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Blocks a user from the doer. + /// + /// - `username`: username of the user + pub async fn user_block_user(&self, username: &str) -> Result<(), ForgejoError> { + let request = self.put(&format!("user/block/{username}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's email addresses + pub async fn user_list_emails(&self) -> Result<Vec<Email>, ForgejoError> { + let request = self.get("user/emails").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Add email addresses + /// + /// - `body`: See [`CreateEmailOption`] + pub async fn user_add_email( + &self, + body: CreateEmailOption, + ) -> Result<Vec<Email>, ForgejoError> { + let request = self.post("user/emails").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete email addresses + /// + /// - `body`: See [`DeleteEmailOption`] + pub async fn user_delete_email(&self, body: DeleteEmailOption) -> Result<(), ForgejoError> { + let request = self.delete("user/emails").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's followers + /// + pub async fn user_current_list_followers( + &self, + query: UserCurrentListFollowersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self.get(&format!("user/followers?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the users that the authenticated user is following + /// + pub async fn user_current_list_following( + &self, + query: UserCurrentListFollowingQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self.get(&format!("user/following?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check whether a user is followed by the authenticated user + /// + /// - `username`: username of followed user + pub async fn user_current_check_following(&self, username: &str) -> Result<(), ForgejoError> { + let request = self.get(&format!("user/following/{username}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Follow a user + /// + /// - `username`: username of user to follow + pub async fn user_current_put_follow(&self, username: &str) -> Result<(), ForgejoError> { + let request = self.put(&format!("user/following/{username}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unfollow a user + /// + /// - `username`: username of user to unfollow + pub async fn user_current_delete_follow(&self, username: &str) -> Result<(), ForgejoError> { + let request = self.delete(&format!("user/following/{username}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a Token to verify + pub async fn get_verification_token(&self) -> Result<String, ForgejoError> { + let request = self.get("user/gpg_key_token").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.text().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Verify a GPG key + pub async fn user_verify_gpg_key(&self) -> Result<GPGKey, ForgejoError> { + let request = self.post("user/gpg_key_verify").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's GPG keys + /// + pub async fn user_current_list_gpg_keys( + &self, + query: UserCurrentListGpgKeysQuery, + ) -> Result<Vec<GPGKey>, ForgejoError> { + let request = self.get(&format!("user/gpg_keys?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a GPG key + /// + /// - `Form`: See [`CreateGPGKeyOption`] + pub async fn user_current_post_gpg_key( + &self, + form: CreateGPGKeyOption, + ) -> Result<GPGKey, ForgejoError> { + let request = self.post("user/gpg_keys").json(&form).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a GPG key + /// + /// - `id`: id of key to get + pub async fn user_current_get_gpg_key(&self, id: u64) -> Result<GPGKey, ForgejoError> { + let request = self.get(&format!("user/gpg_keys/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Remove a GPG key + /// + /// - `id`: id of key to delete + pub async fn user_current_delete_gpg_key(&self, id: u64) -> Result<(), ForgejoError> { + let request = self.delete(&format!("user/gpg_keys/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's webhooks + /// + pub async fn user_list_hooks( + &self, + query: UserListHooksQuery, + ) -> Result<Vec<Hook>, ForgejoError> { + let request = self.get(&format!("user/hooks?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a hook + /// + /// - `body`: See [`CreateHookOption`] + pub async fn user_create_hook(&self, body: CreateHookOption) -> Result<Hook, ForgejoError> { + let request = self.post("user/hooks").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a hook + /// + /// - `id`: id of the hook to get + pub async fn user_get_hook(&self, id: u64) -> Result<Hook, ForgejoError> { + let request = self.get(&format!("user/hooks/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a hook + /// + /// - `id`: id of the hook to delete + pub async fn user_delete_hook(&self, id: u64) -> Result<(), ForgejoError> { + let request = self.delete(&format!("user/hooks/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update a hook + /// + /// - `id`: id of the hook to update + /// - `body`: See [`EditHookOption`] + pub async fn user_edit_hook( + &self, + id: u64, + body: EditHookOption, + ) -> Result<Hook, ForgejoError> { + let request = self + .patch(&format!("user/hooks/{id}")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's public keys + /// + pub async fn user_current_list_keys( + &self, + query: UserCurrentListKeysQuery, + ) -> Result<Vec<PublicKey>, ForgejoError> { + let request = self.get(&format!("user/keys?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a public key + /// + /// - `body`: See [`CreateKeyOption`] + pub async fn user_current_post_key( + &self, + body: CreateKeyOption, + ) -> Result<PublicKey, ForgejoError> { + let request = self.post("user/keys").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a public key + /// + /// - `id`: id of key to get + pub async fn user_current_get_key(&self, id: u64) -> Result<PublicKey, ForgejoError> { + let request = self.get(&format!("user/keys/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Delete a public key + /// + /// - `id`: id of key to delete + pub async fn user_current_delete_key(&self, id: u64) -> Result<(), ForgejoError> { + let request = self.delete(&format!("user/keys/{id}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's blocked users + /// + pub async fn user_list_blocked_users( + &self, + query: UserListBlockedUsersQuery, + ) -> Result<Vec<BlockedUser>, ForgejoError> { + let request = self.get(&format!("user/list_blocked?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the current user's organizations + /// + pub async fn org_list_current_user_orgs( + &self, + query: OrgListCurrentUserOrgsQuery, + ) -> Result<Vec<Organization>, ForgejoError> { + let request = self.get(&format!("user/orgs?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the repos that the authenticated user owns + /// + pub async fn user_current_list_repos( + &self, + query: UserCurrentListReposQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self.get(&format!("user/repos?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create a repository + /// + /// - `body`: See [`CreateRepoOption`] + pub async fn create_current_user_repo( + &self, + body: CreateRepoOption, + ) -> Result<Repository, ForgejoError> { + let request = self.post("user/repos").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get user settings + pub async fn get_user_settings(&self) -> Result<Vec<UserSettings>, ForgejoError> { + let request = self.get("user/settings").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Update user settings + /// + /// - `body`: See [`UserSettingsOptions`] + pub async fn update_user_settings( + &self, + body: UserSettingsOptions, + ) -> Result<Vec<UserSettings>, ForgejoError> { + let request = self.patch("user/settings").json(&body).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// The repos that the authenticated user has starred + /// + pub async fn user_current_list_starred( + &self, + query: UserCurrentListStarredQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self.get(&format!("user/starred?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Whether the authenticated is starring the repo + /// + /// - `owner`: owner of the repo + /// - `repo`: name of the repo + pub async fn user_current_check_starring( + &self, + owner: &str, + repo: &str, + ) -> Result<(), ForgejoError> { + let request = self.get(&format!("user/starred/{owner}/{repo}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Star the given repo + /// + /// - `owner`: owner of the repo to star + /// - `repo`: name of the repo to star + pub async fn user_current_put_star(&self, owner: &str, repo: &str) -> Result<(), ForgejoError> { + let request = self.put(&format!("user/starred/{owner}/{repo}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unstar the given repo + /// + /// - `owner`: owner of the repo to unstar + /// - `repo`: name of the repo to unstar + pub async fn user_current_delete_star( + &self, + owner: &str, + repo: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("user/starred/{owner}/{repo}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get list of all existing stopwatches + /// + pub async fn user_get_stop_watches( + &self, + query: UserGetStopWatchesQuery, + ) -> Result<Vec<StopWatch>, ForgejoError> { + let request = self.get(&format!("user/stopwatches?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List repositories watched by the authenticated user + /// + pub async fn user_current_list_subscriptions( + &self, + query: UserCurrentListSubscriptionsQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self.get(&format!("user/subscriptions?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List all the teams a user belongs to + /// + pub async fn user_list_teams( + &self, + query: UserListTeamsQuery, + ) -> Result<Vec<Team>, ForgejoError> { + let request = self.get(&format!("user/teams?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the current user's tracked times + /// + pub async fn user_current_tracked_times( + &self, + query: UserCurrentTrackedTimesQuery, + ) -> Result<Vec<TrackedTime>, ForgejoError> { + let request = self.get(&format!("user/times?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Unblocks a user from the doer. + /// + /// - `username`: username of the user + pub async fn user_unblock_user(&self, username: &str) -> Result<(), ForgejoError> { + let request = self.put(&format!("user/unblock/{username}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Search for users + /// + pub async fn user_search( + &self, + query: UserSearchQuery, + ) -> Result<UserSearchResponse, ForgejoError> { + let request = self.get(&format!("users/search?{query}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a user + /// + /// - `username`: username of user to get + pub async fn user_get(&self, username: &str) -> Result<User, ForgejoError> { + let request = self.get(&format!("users/{username}")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a user's activity feeds + /// + /// - `username`: username of user + pub async fn user_list_activity_feeds( + &self, + username: &str, + query: UserListActivityFeedsQuery, + ) -> Result<Vec<Activity>, ForgejoError> { + let request = self + .get(&format!("users/{username}/activities/feeds?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the given user's followers + /// + /// - `username`: username of user + pub async fn user_list_followers( + &self, + username: &str, + query: UserListFollowersQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("users/{username}/followers?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the users that the given user is following + /// + /// - `username`: username of user + pub async fn user_list_following( + &self, + username: &str, + query: UserListFollowingQuery, + ) -> Result<Vec<User>, ForgejoError> { + let request = self + .get(&format!("users/{username}/following?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Check if one user is following another user + /// + /// - `username`: username of following user + /// - `target`: username of followed user + pub async fn user_check_following( + &self, + username: &str, + target: &str, + ) -> Result<(), ForgejoError> { + let request = self + .get(&format!("users/{username}/following/{target}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the given user's GPG keys + /// + /// - `username`: username of user + pub async fn user_list_gpg_keys( + &self, + username: &str, + query: UserListGpgKeysQuery, + ) -> Result<Vec<GPGKey>, ForgejoError> { + let request = self + .get(&format!("users/{username}/gpg_keys?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get a user's heatmap + /// + /// - `username`: username of user to get + pub async fn user_get_heatmap_data( + &self, + username: &str, + ) -> Result<Vec<UserHeatmapData>, ForgejoError> { + let request = self.get(&format!("users/{username}/heatmap")).build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the given user's public keys + /// + /// - `username`: username of user + pub async fn user_list_keys( + &self, + username: &str, + query: UserListKeysQuery, + ) -> Result<Vec<PublicKey>, ForgejoError> { + let request = self + .get(&format!("users/{username}/keys?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List a user's organizations + /// + /// - `username`: username of user + pub async fn org_list_user_orgs( + &self, + username: &str, + query: OrgListUserOrgsQuery, + ) -> Result<Vec<Organization>, ForgejoError> { + let request = self + .get(&format!("users/{username}/orgs?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Get user permissions in organization + /// + /// - `username`: username of user + /// - `org`: name of the organization + pub async fn org_get_user_permissions( + &self, + username: &str, + org: &str, + ) -> Result<OrganizationPermissions, ForgejoError> { + let request = self + .get(&format!("users/{username}/orgs/{org}/permissions")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the repos owned by the given user + /// + /// - `username`: username of user + pub async fn user_list_repos( + &self, + username: &str, + query: UserListReposQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self + .get(&format!("users/{username}/repos?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// The repos that the given user has starred + /// + /// - `username`: username of user + pub async fn user_list_starred( + &self, + username: &str, + query: UserListStarredQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self + .get(&format!("users/{username}/starred?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the repositories watched by a user + /// + /// - `username`: username of the user + pub async fn user_list_subscriptions( + &self, + username: &str, + query: UserListSubscriptionsQuery, + ) -> Result<Vec<Repository>, ForgejoError> { + let request = self + .get(&format!("users/{username}/subscriptions?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// List the authenticated user's access tokens + /// + /// - `username`: username of user + pub async fn user_get_tokens( + &self, + username: &str, + query: UserGetTokensQuery, + ) -> Result<Vec<AccessToken>, ForgejoError> { + let request = self + .get(&format!("users/{username}/tokens?{query}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Create an access token + /// + /// - `username`: username of user + /// - `body`: See [`CreateAccessTokenOption`] + pub async fn user_create_token( + &self, + username: &str, + body: CreateAccessTokenOption, + ) -> Result<AccessToken, ForgejoError> { + let request = self + .post(&format!("users/{username}/tokens")) + .json(&body) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 201 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// delete an access token + /// + /// - `username`: username of user + /// - `token`: token to be deleted, identified by ID and if not available by name + pub async fn user_delete_access_token( + &self, + username: &str, + token: &str, + ) -> Result<(), ForgejoError> { + let request = self + .delete(&format!("users/{username}/tokens/{token}")) + .build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 204 => Ok(()), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } + + /// Returns the version of the Gitea application + pub async fn get_version(&self) -> Result<ServerVersion, ForgejoError> { + let request = self.get("version").build()?; + let response = self.execute(request).await?; + match response.status().as_u16() { + 200 => Ok(response.json().await?), + _ => Err(ForgejoError::UnexpectedStatusCode(response.status())), + } + } +} diff --git a/src/generated/mod.rs b/src/generated/mod.rs new file mode 100644 index 0000000..fb6927b --- /dev/null +++ b/src/generated/mod.rs @@ -0,0 +1,2 @@ +pub mod methods; +pub mod structs; diff --git a/src/generated/structs.rs b/src/generated/structs.rs new file mode 100644 index 0000000..7b8b25f --- /dev/null +++ b/src/generated/structs.rs @@ -0,0 +1,5959 @@ +use crate::StructureError; +use std::collections::BTreeMap; +/// APIError is an api error with a message +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct APIError { + pub message: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct AccessToken { + pub id: Option<u64>, + pub name: Option<String>, + pub scopes: Option<Vec<String>>, + pub sha1: Option<String>, + pub token_last_eight: Option<String>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Activity { + pub act_user: Option<User>, + pub act_user_id: Option<u64>, + pub comment: Option<Comment>, + pub comment_id: Option<u64>, + pub content: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + pub id: Option<u64>, + pub is_private: Option<bool>, + pub op_type: Option<String>, + pub ref_name: Option<String>, + pub repo: Option<Repository>, + pub repo_id: Option<u64>, + pub user_id: Option<u64>, +} + +/// ActivityPub type +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ActivityPub { + #[serde(rename = "@context")] + pub context: Option<String>, +} + +/// AddCollaboratorOption options when adding a user as a collaborator of a repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct AddCollaboratorOption { + pub permission: Option<String>, +} + +/// AddTimeOption options for adding time to an issue +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct AddTimeOption { + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + /// time in seconds + pub time: u64, + /// User who spent the time (optional) + pub user_name: Option<String>, +} + +/// AnnotatedTag represents an annotated tag +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct AnnotatedTag { + pub message: Option<String>, + pub object: Option<AnnotatedTagObject>, + pub sha: Option<String>, + pub tag: Option<String>, + pub tagger: Option<CommitUser>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + pub verification: Option<PayloadCommitVerification>, +} + +/// AnnotatedTagObject contains meta information of the tag object +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct AnnotatedTagObject { + pub sha: Option<String>, + #[serde(rename = "type")] + pub r#type: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// Attachment a generic attachment +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Attachment { + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub browser_download_url: Option<url::Url>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub download_count: Option<u64>, + pub id: Option<u64>, + pub name: Option<String>, + pub size: Option<u64>, + pub uuid: Option<String>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct BlockedUser { + pub block_id: Option<u64>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, +} + +/// Branch represents a repository branch +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Branch { + pub commit: Option<PayloadCommit>, + pub effective_branch_protection_name: Option<String>, + pub enable_status_check: Option<bool>, + pub name: Option<String>, + pub protected: Option<bool>, + pub required_approvals: Option<u64>, + pub status_check_contexts: Option<Vec<String>>, + pub user_can_merge: Option<bool>, + pub user_can_push: Option<bool>, +} + +/// BranchProtection represents a branch protection for a repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct BranchProtection { + pub approvals_whitelist_teams: Option<Vec<String>>, + pub approvals_whitelist_username: Option<Vec<String>>, + pub block_on_official_review_requests: Option<bool>, + pub block_on_outdated_branch: Option<bool>, + pub block_on_rejected_reviews: Option<bool>, + /// Deprecated: true + pub branch_name: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub dismiss_stale_approvals: Option<bool>, + pub enable_approvals_whitelist: Option<bool>, + pub enable_merge_whitelist: Option<bool>, + pub enable_push: Option<bool>, + pub enable_push_whitelist: Option<bool>, + pub enable_status_check: Option<bool>, + pub merge_whitelist_teams: Option<Vec<String>>, + pub merge_whitelist_usernames: Option<Vec<String>>, + pub protected_file_patterns: Option<String>, + pub push_whitelist_deploy_keys: Option<bool>, + pub push_whitelist_teams: Option<Vec<String>>, + pub push_whitelist_usernames: Option<Vec<String>>, + pub require_signed_commits: Option<bool>, + pub required_approvals: Option<u64>, + pub rule_name: Option<String>, + pub status_check_contexts: Option<Vec<String>>, + pub unprotected_file_patterns: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// ChangeFileOperation for creating, updating or deleting a file +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ChangeFileOperation { + /// new or updated file content, must be base64 encoded + pub content: Option<String>, + /// old path of the file to move + pub from_path: Option<String>, + /// indicates what to do with the file + pub operation: ChangeFileOperationOperation, + /// path to the existing or new file + pub path: String, + /// sha is the SHA for the file that already exists, required for update or delete + pub sha: Option<String>, +} + +/// indicates what to do with the file + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum ChangeFileOperationOperation { + #[serde(rename = "create")] + Create, + #[serde(rename = "update")] + Update, + #[serde(rename = "delete")] + Delete, +} +/// ChangeFilesOptions options for creating, updating or deleting multiple files +/// +/// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ChangeFilesOptions { + pub author: Option<Identity>, + /// branch (optional) to base this file from. if not given, the default branch is used + pub branch: Option<String>, + pub committer: Option<Identity>, + pub dates: Option<CommitDateOptions>, + /// list of file operations + pub files: Vec<ChangeFileOperation>, + /// message (optional) for the commit of this file. if not supplied, a default message will be used + pub message: Option<String>, + /// new_branch (optional) will make a new branch from `branch` before creating the file + pub new_branch: Option<String>, + /// Add a Signed-off-by trailer by the committer at the end of the commit log message. + pub signoff: Option<bool>, +} + +/// ChangedFile store information about files affected by the pull request +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ChangedFile { + pub additions: Option<u64>, + pub changes: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub contents_url: Option<url::Url>, + pub deletions: Option<u64>, + pub filename: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub previous_filename: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub raw_url: Option<url::Url>, + pub status: Option<String>, +} + +/// CombinedStatus holds the combined state of several statuses for a single commit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CombinedStatus { + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub commit_url: Option<url::Url>, + pub repository: Option<Repository>, + pub sha: Option<String>, + pub state: Option<String>, + pub statuses: Option<Vec<CommitStatus>>, + pub total_count: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// Comment represents a comment on a commit or issue +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Comment { + pub assets: Option<Vec<Attachment>>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub issue_url: Option<url::Url>, + pub original_author: Option<String>, + pub original_author_id: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub pull_request_url: Option<url::Url>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + pub user: Option<User>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Commit { + pub author: Option<User>, + pub commit: Option<RepoCommit>, + pub committer: Option<User>, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + pub files: Option<Vec<CommitAffectedFiles>>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub parents: Option<Vec<CommitMeta>>, + pub sha: Option<String>, + pub stats: Option<CommitStats>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// CommitAffectedFiles store information about files affected by the commit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CommitAffectedFiles { + pub filename: Option<String>, + pub status: Option<String>, +} + +/// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CommitDateOptions { + #[serde(with = "time::serde::rfc3339::option")] + pub author: Option<time::OffsetDateTime>, + #[serde(with = "time::serde::rfc3339::option")] + pub committer: Option<time::OffsetDateTime>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CommitMeta { + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + pub sha: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// CommitStats is statistics for a RepoCommit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CommitStats { + pub additions: Option<u64>, + pub deletions: Option<u64>, + pub total: Option<u64>, +} + +/// CommitStatus holds a single status of a single Commit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CommitStatus { + pub context: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub creator: Option<User>, + pub description: Option<String>, + pub id: Option<u64>, + pub status: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub target_url: Option<url::Url>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// CommitStatusState holds the state of a CommitStatus +/// +/// It can be "pending", "success", "error" and "failure" +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CommitStatusState {} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CommitUser { + pub date: Option<String>, + pub email: Option<String>, + pub name: Option<String>, +} + +/// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ContentsResponse { + #[serde(rename = "_links")] + pub links: Option<FileLinksResponse>, + /// `content` is populated when `type` is `file`, otherwise null + pub content: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub download_url: Option<url::Url>, + /// `encoding` is populated when `type` is `file`, otherwise null + pub encoding: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub git_url: Option<url::Url>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub last_commit_sha: Option<String>, + pub name: Option<String>, + pub path: Option<String>, + pub sha: Option<String>, + pub size: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + /// `submodule_git_url` is populated when `type` is `submodule`, otherwise null + pub submodule_git_url: Option<url::Url>, + /// `target` is populated when `type` is `symlink`, otherwise null + pub target: Option<String>, + /// `type` will be `file`, `dir`, `symlink`, or `submodule` + #[serde(rename = "type")] + pub r#type: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// CreateAccessTokenOption options when create access token +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateAccessTokenOption { + pub name: String, + pub scopes: Option<Vec<String>>, +} + +/// CreateBranchProtectionOption options for creating a branch protection +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateBranchProtectionOption { + pub approvals_whitelist_teams: Option<Vec<String>>, + pub approvals_whitelist_username: Option<Vec<String>>, + pub block_on_official_review_requests: Option<bool>, + pub block_on_outdated_branch: Option<bool>, + pub block_on_rejected_reviews: Option<bool>, + /// Deprecated: true + pub branch_name: Option<String>, + pub dismiss_stale_approvals: Option<bool>, + pub enable_approvals_whitelist: Option<bool>, + pub enable_merge_whitelist: Option<bool>, + pub enable_push: Option<bool>, + pub enable_push_whitelist: Option<bool>, + pub enable_status_check: Option<bool>, + pub merge_whitelist_teams: Option<Vec<String>>, + pub merge_whitelist_usernames: Option<Vec<String>>, + pub protected_file_patterns: Option<String>, + pub push_whitelist_deploy_keys: Option<bool>, + pub push_whitelist_teams: Option<Vec<String>>, + pub push_whitelist_usernames: Option<Vec<String>>, + pub require_signed_commits: Option<bool>, + pub required_approvals: Option<u64>, + pub rule_name: Option<String>, + pub status_check_contexts: Option<Vec<String>>, + pub unprotected_file_patterns: Option<String>, +} + +/// CreateBranchRepoOption options when creating a branch in a repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateBranchRepoOption { + /// Name of the branch to create + pub new_branch_name: String, + /// Deprecated: true + /// + /// Name of the old branch to create from + pub old_branch_name: Option<String>, + /// Name of the old branch/tag/commit to create from + pub old_ref_name: Option<String>, +} + +/// CreateEmailOption options when creating email addresses +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateEmailOption { + /// email addresses to add + pub emails: Option<Vec<String>>, +} + +/// CreateFileOptions options for creating files +/// +/// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateFileOptions { + pub author: Option<Identity>, + /// branch (optional) to base this file from. if not given, the default branch is used + pub branch: Option<String>, + pub committer: Option<Identity>, + /// content must be base64 encoded + pub content: String, + pub dates: Option<CommitDateOptions>, + /// message (optional) for the commit of this file. if not supplied, a default message will be used + pub message: Option<String>, + /// new_branch (optional) will make a new branch from `branch` before creating the file + pub new_branch: Option<String>, + /// Add a Signed-off-by trailer by the committer at the end of the commit log message. + pub signoff: Option<bool>, +} + +/// CreateForkOption options for creating a fork +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateForkOption { + /// name of the forked repository + pub name: Option<String>, + /// organization name, if forking into an organization + pub organization: Option<String>, +} + +/// CreateGPGKeyOption options create user GPG key +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateGPGKeyOption { + /// An armored GPG key to add + pub armored_public_key: String, + pub armored_signature: Option<String>, +} + +/// CreateHookOption options when create a hook +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateHookOption { + pub active: Option<bool>, + pub authorization_header: Option<String>, + pub branch_filter: Option<String>, + pub config: CreateHookOptionConfig, + pub events: Option<Vec<String>>, + #[serde(rename = "type")] + pub r#type: CreateHookOptionType, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum CreateHookOptionType { + #[serde(rename = "forgejo")] + Forgejo, + #[serde(rename = "dingtalk")] + Dingtalk, + #[serde(rename = "discord")] + Discord, + #[serde(rename = "gitea")] + Gitea, + #[serde(rename = "gogs")] + Gogs, + #[serde(rename = "msteams")] + Msteams, + #[serde(rename = "slack")] + Slack, + #[serde(rename = "telegram")] + Telegram, + #[serde(rename = "feishu")] + Feishu, + #[serde(rename = "wechatwork")] + Wechatwork, + #[serde(rename = "packagist")] + Packagist, +} +/// CreateHookOptionConfig has all config options in it +/// +/// required are "content_type" and "url" Required +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateHookOptionConfig { + #[serde(flatten)] + pub additional: BTreeMap<String, String>, +} + +/// CreateIssueCommentOption options for creating a comment on an issue +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateIssueCommentOption { + pub body: String, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// CreateIssueOption options to create one issue +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateIssueOption { + /// deprecated + pub assignee: Option<String>, + pub assignees: Option<Vec<String>>, + pub body: Option<String>, + pub closed: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_date: Option<time::OffsetDateTime>, + /// list of label ids + pub labels: Option<Vec<u64>>, + /// milestone id + pub milestone: Option<u64>, + #[serde(rename = "ref")] + pub r#ref: Option<String>, + pub title: String, +} + +/// CreateKeyOption options when creating a key +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateKeyOption { + /// An armored SSH key to add + pub key: String, + /// Describe if the key has only read access or read/write + pub read_only: Option<bool>, + /// Title of the key to add + pub title: String, +} + +/// CreateLabelOption options for creating a label +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateLabelOption { + pub color: String, + pub description: Option<String>, + pub exclusive: Option<bool>, + pub is_archived: Option<bool>, + pub name: String, +} + +/// CreateMilestoneOption options for creating a milestone +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateMilestoneOption { + pub description: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_on: Option<time::OffsetDateTime>, + pub state: Option<CreateMilestoneOptionState>, + pub title: Option<String>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum CreateMilestoneOptionState { + #[serde(rename = "open")] + Open, + #[serde(rename = "closed")] + Closed, +} +/// CreateOAuth2ApplicationOptions holds options to create an oauth2 application +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateOAuth2ApplicationOptions { + pub confidential_client: Option<bool>, + pub name: Option<String>, + pub redirect_uris: Option<Vec<String>>, +} + +/// CreateOrUpdateSecretOption options when creating or updating secret +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateOrUpdateSecretOption { + /// Data of the secret to update + pub data: String, +} + +/// CreateOrgOption options for creating an organization +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateOrgOption { + pub description: Option<String>, + pub email: Option<String>, + pub full_name: Option<String>, + pub location: Option<String>, + pub repo_admin_change_team_access: Option<bool>, + pub username: String, + /// possible values are `public` (default), `limited` or `private` + pub visibility: Option<CreateOrgOptionVisibility>, + pub website: Option<String>, +} + +/// possible values are `public` (default), `limited` or `private` + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum CreateOrgOptionVisibility { + #[serde(rename = "public")] + Public, + #[serde(rename = "limited")] + Limited, + #[serde(rename = "private")] + Private, +} +/// CreatePullRequestOption options when creating a pull request +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreatePullRequestOption { + pub assignee: Option<String>, + pub assignees: Option<Vec<String>>, + pub base: Option<String>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_date: Option<time::OffsetDateTime>, + pub head: Option<String>, + pub labels: Option<Vec<u64>>, + pub milestone: Option<u64>, + pub title: Option<String>, +} + +/// CreatePullReviewComment represent a review comment for creation api +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreatePullReviewComment { + pub body: Option<String>, + /// if comment to new file line or 0 + pub new_position: Option<u64>, + /// if comment to old file line or 0 + pub old_position: Option<u64>, + /// the tree path + pub path: Option<String>, +} + +/// CreatePullReviewOptions are options to create a pull review +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreatePullReviewOptions { + pub body: Option<String>, + pub comments: Option<Vec<CreatePullReviewComment>>, + pub commit_id: Option<String>, + pub event: Option<String>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreatePushMirrorOption { + pub interval: Option<String>, + pub remote_address: Option<String>, + pub remote_password: Option<String>, + pub remote_username: Option<String>, + pub sync_on_commit: Option<bool>, +} + +/// CreateReleaseOption options when creating a release +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateReleaseOption { + pub body: Option<String>, + pub draft: Option<bool>, + pub name: Option<String>, + pub prerelease: Option<bool>, + pub tag_name: String, + pub target_commitish: Option<String>, +} + +/// CreateRepoOption options when creating repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateRepoOption { + /// Whether the repository should be auto-initialized? + pub auto_init: Option<bool>, + /// DefaultBranch of the repository (used when initializes and in template) + pub default_branch: Option<String>, + /// Description of the repository to create + pub description: Option<String>, + /// Gitignores to use + pub gitignores: Option<String>, + /// Label-Set to use + pub issue_labels: Option<String>, + /// License to use + pub license: Option<String>, + /// Name of the repository to create + pub name: String, + /// Whether the repository is private + pub private: Option<bool>, + /// Readme of the repository to create + pub readme: Option<String>, + /// Whether the repository is template + pub template: Option<bool>, + /// TrustModel of the repository + pub trust_model: Option<CreateRepoOptionTrustModel>, +} + +/// TrustModel of the repository + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum CreateRepoOptionTrustModel { + #[serde(rename = "default")] + Default, + #[serde(rename = "collaborator")] + Collaborator, + #[serde(rename = "committer")] + Committer, + #[serde(rename = "collaboratorcommitter")] + Collaboratorcommitter, +} +/// CreateStatusOption holds the information needed to create a new CommitStatus for a Commit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateStatusOption { + pub context: Option<String>, + pub description: Option<String>, + pub state: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub target_url: Option<url::Url>, +} + +/// CreateTagOption options when creating a tag +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateTagOption { + pub message: Option<String>, + pub tag_name: String, + pub target: Option<String>, +} + +/// CreateTeamOption options for creating a team +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateTeamOption { + pub can_create_org_repo: Option<bool>, + pub description: Option<String>, + pub includes_all_repositories: Option<bool>, + pub name: String, + pub permission: Option<CreateTeamOptionPermission>, + pub units: Option<Vec<String>>, + pub units_map: Option<BTreeMap<String, String>>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum CreateTeamOptionPermission { + #[serde(rename = "read")] + Read, + #[serde(rename = "write")] + Write, + #[serde(rename = "admin")] + Admin, +} +/// CreateUserOption create user options +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateUserOption { + #[serde(with = "time::serde::rfc3339::option")] + /// For explicitly setting the user creation timestamp. Useful when users are + /// + /// migrated from other systems. When omitted, the user's creation timestamp + /// + /// will be set to "now". + pub created_at: Option<time::OffsetDateTime>, + pub email: String, + pub full_name: Option<String>, + pub login_name: Option<String>, + pub must_change_password: Option<bool>, + pub password: Option<String>, + pub restricted: Option<bool>, + pub send_notify: Option<bool>, + pub source_id: Option<u64>, + pub username: String, + pub visibility: Option<String>, +} + +/// CreateWikiPageOptions form for creating wiki +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct CreateWikiPageOptions { + /// content must be base64 encoded + pub content_base64: Option<String>, + /// optional commit message summarizing the change + pub message: Option<String>, + /// page title. leave empty to keep unchanged + pub title: Option<String>, +} + +/// Cron represents a Cron task +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Cron { + pub exec_times: Option<u64>, + pub name: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub next: Option<time::OffsetDateTime>, + #[serde(with = "time::serde::rfc3339::option")] + pub prev: Option<time::OffsetDateTime>, + pub schedule: Option<String>, +} + +/// DeleteEmailOption options when deleting email addresses +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct DeleteEmailOption { + /// email addresses to delete + pub emails: Option<Vec<String>>, +} + +/// DeleteFileOptions options for deleting files (used for other File structs below) +/// +/// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct DeleteFileOptions { + pub author: Option<Identity>, + /// branch (optional) to base this file from. if not given, the default branch is used + pub branch: Option<String>, + pub committer: Option<Identity>, + pub dates: Option<CommitDateOptions>, + /// message (optional) for the commit of this file. if not supplied, a default message will be used + pub message: Option<String>, + /// new_branch (optional) will make a new branch from `branch` before creating the file + pub new_branch: Option<String>, + /// sha is the SHA for the file that already exists + pub sha: String, + /// Add a Signed-off-by trailer by the committer at the end of the commit log message. + pub signoff: Option<bool>, +} + +/// DeleteLabelOption options for deleting a label +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct DeleteLabelsOption { + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// DeployKey a deploy key +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct DeployKey { + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub fingerprint: Option<String>, + pub id: Option<u64>, + pub key: Option<String>, + pub key_id: Option<u64>, + pub read_only: Option<bool>, + pub repository: Option<Repository>, + pub title: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// DismissPullReviewOptions are options to dismiss a pull review +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct DismissPullReviewOptions { + pub message: Option<String>, + pub priors: Option<bool>, +} + +/// EditAttachmentOptions options for editing attachments +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditAttachmentOptions { + pub name: Option<String>, +} + +/// EditBranchProtectionOption options for editing a branch protection +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditBranchProtectionOption { + pub approvals_whitelist_teams: Option<Vec<String>>, + pub approvals_whitelist_username: Option<Vec<String>>, + pub block_on_official_review_requests: Option<bool>, + pub block_on_outdated_branch: Option<bool>, + pub block_on_rejected_reviews: Option<bool>, + pub dismiss_stale_approvals: Option<bool>, + pub enable_approvals_whitelist: Option<bool>, + pub enable_merge_whitelist: Option<bool>, + pub enable_push: Option<bool>, + pub enable_push_whitelist: Option<bool>, + pub enable_status_check: Option<bool>, + pub merge_whitelist_teams: Option<Vec<String>>, + pub merge_whitelist_usernames: Option<Vec<String>>, + pub protected_file_patterns: Option<String>, + pub push_whitelist_deploy_keys: Option<bool>, + pub push_whitelist_teams: Option<Vec<String>>, + pub push_whitelist_usernames: Option<Vec<String>>, + pub require_signed_commits: Option<bool>, + pub required_approvals: Option<u64>, + pub status_check_contexts: Option<Vec<String>>, + pub unprotected_file_patterns: Option<String>, +} + +/// EditDeadlineOption options for creating a deadline +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditDeadlineOption { + #[serde(with = "time::serde::rfc3339")] + pub due_date: time::OffsetDateTime, +} + +/// EditGitHookOption options when modifying one Git hook +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditGitHookOption { + pub content: Option<String>, +} + +/// EditHookOption options when modify one hook +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditHookOption { + pub active: Option<bool>, + pub authorization_header: Option<String>, + pub branch_filter: Option<String>, + pub config: Option<BTreeMap<String, String>>, + pub events: Option<Vec<String>>, +} + +/// EditIssueCommentOption options for editing a comment +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditIssueCommentOption { + pub body: String, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// EditIssueOption options for editing an issue +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditIssueOption { + /// deprecated + pub assignee: Option<String>, + pub assignees: Option<Vec<String>>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_date: Option<time::OffsetDateTime>, + pub milestone: Option<u64>, + #[serde(rename = "ref")] + pub r#ref: Option<String>, + pub state: Option<String>, + pub title: Option<String>, + pub unset_due_date: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// EditLabelOption options for editing a label +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditLabelOption { + pub color: Option<String>, + pub description: Option<String>, + pub exclusive: Option<bool>, + pub is_archived: Option<bool>, + pub name: Option<String>, +} + +/// EditMilestoneOption options for editing a milestone +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditMilestoneOption { + pub description: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_on: Option<time::OffsetDateTime>, + pub state: Option<String>, + pub title: Option<String>, +} + +/// EditOrgOption options for editing an organization +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditOrgOption { + pub description: Option<String>, + pub email: Option<String>, + pub full_name: Option<String>, + pub location: Option<String>, + pub repo_admin_change_team_access: Option<bool>, + /// possible values are `public`, `limited` or `private` + pub visibility: Option<EditOrgOptionVisibility>, + pub website: Option<String>, +} + +/// possible values are `public`, `limited` or `private` + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum EditOrgOptionVisibility { + #[serde(rename = "public")] + Public, + #[serde(rename = "limited")] + Limited, + #[serde(rename = "private")] + Private, +} +/// EditPullRequestOption options when modify pull request +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditPullRequestOption { + pub allow_maintainer_edit: Option<bool>, + pub assignee: Option<String>, + pub assignees: Option<Vec<String>>, + pub base: Option<String>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_date: Option<time::OffsetDateTime>, + pub labels: Option<Vec<u64>>, + pub milestone: Option<u64>, + pub state: Option<String>, + pub title: Option<String>, + pub unset_due_date: Option<bool>, +} + +/// EditReactionOption contain the reaction type +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditReactionOption { + pub content: Option<String>, +} + +/// EditReleaseOption options when editing a release +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditReleaseOption { + pub body: Option<String>, + pub draft: Option<bool>, + pub name: Option<String>, + pub prerelease: Option<bool>, + pub tag_name: Option<String>, + pub target_commitish: Option<String>, +} + +/// EditRepoOption options when editing a repository's properties +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditRepoOption { + /// either `true` to allow mark pr as merged manually, or `false` to prevent it. + pub allow_manual_merge: Option<bool>, + /// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. + pub allow_merge_commits: Option<bool>, + /// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. + pub allow_rebase: Option<bool>, + /// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. + pub allow_rebase_explicit: Option<bool>, + /// either `true` to allow updating pull request branch by rebase, or `false` to prevent it. + pub allow_rebase_update: Option<bool>, + /// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. + pub allow_squash_merge: Option<bool>, + /// set to `true` to archive this repository. + pub archived: Option<bool>, + /// either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur. + pub autodetect_manual_merge: Option<bool>, + /// set to `true` to allow edits from maintainers by default + pub default_allow_maintainer_edit: Option<bool>, + /// sets the default branch for this repository. + pub default_branch: Option<String>, + /// set to `true` to delete pr branch after merge by default + pub default_delete_branch_after_merge: Option<bool>, + /// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". + pub default_merge_style: Option<String>, + /// a short description of the repository. + pub description: Option<String>, + /// enable prune - remove obsolete remote-tracking references + pub enable_prune: Option<bool>, + pub external_tracker: Option<ExternalTracker>, + pub external_wiki: Option<ExternalWiki>, + /// either `true` to enable actions unit, or `false` to disable them. + pub has_actions: Option<bool>, + /// either `true` to enable issues for this repository or `false` to disable them. + pub has_issues: Option<bool>, + /// either `true` to enable packages unit, or `false` to disable them. + pub has_packages: Option<bool>, + /// either `true` to enable project unit, or `false` to disable them. + pub has_projects: Option<bool>, + /// either `true` to allow pull requests, or `false` to prevent pull request. + pub has_pull_requests: Option<bool>, + /// either `true` to enable releases unit, or `false` to disable them. + pub has_releases: Option<bool>, + /// either `true` to enable the wiki for this repository or `false` to disable it. + pub has_wiki: Option<bool>, + /// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. + pub ignore_whitespace_conflicts: Option<bool>, + pub internal_tracker: Option<InternalTracker>, + /// set to a string like `8h30m0s` to set the mirror interval time + pub mirror_interval: Option<String>, + /// name of the repository + pub name: Option<String>, + /// either `true` to make the repository private or `false` to make it public. + /// + /// Note: you will get a 422 error if the organization restricts changing repository visibility to organization + /// + /// owners and a non-owner tries to change the value of private. + pub private: Option<bool>, + /// either `true` to make this repository a template or `false` to make it a normal repository + pub template: Option<bool>, + /// a URL with more information about the repository. + pub website: Option<String>, +} + +/// EditTeamOption options for editing a team +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditTeamOption { + pub can_create_org_repo: Option<bool>, + pub description: Option<String>, + pub includes_all_repositories: Option<bool>, + pub name: String, + pub permission: Option<EditTeamOptionPermission>, + pub units: Option<Vec<String>>, + pub units_map: Option<BTreeMap<String, String>>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum EditTeamOptionPermission { + #[serde(rename = "read")] + Read, + #[serde(rename = "write")] + Write, + #[serde(rename = "admin")] + Admin, +} +/// EditUserOption edit user options +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct EditUserOption { + pub active: Option<bool>, + pub admin: Option<bool>, + pub allow_create_organization: Option<bool>, + pub allow_git_hook: Option<bool>, + pub allow_import_local: Option<bool>, + pub description: Option<String>, + pub email: Option<String>, + pub full_name: Option<String>, + pub location: Option<String>, + pub login_name: String, + pub max_repo_creation: Option<u64>, + pub must_change_password: Option<bool>, + pub password: Option<String>, + pub prohibit_login: Option<bool>, + pub restricted: Option<bool>, + pub source_id: u64, + pub visibility: Option<String>, + pub website: Option<String>, +} + +/// Email an email address belonging to a user +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Email { + pub email: Option<String>, + pub primary: Option<bool>, + pub user_id: Option<u64>, + pub username: Option<String>, + pub verified: Option<bool>, +} + +/// ExternalTracker represents settings for external tracker +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ExternalTracker { + /// External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index. + pub external_tracker_format: Option<String>, + /// External Issue Tracker issue regular expression + pub external_tracker_regexp_pattern: Option<String>, + /// External Issue Tracker Number Format, either `numeric`, `alphanumeric`, or `regexp` + pub external_tracker_style: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + /// URL of external issue tracker. + pub external_tracker_url: Option<url::Url>, +} + +/// ExternalWiki represents setting for external wiki +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ExternalWiki { + #[serde(deserialize_with = "crate::none_if_blank_url")] + /// URL of external wiki. + pub external_wiki_url: Option<url::Url>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct FileCommitResponse { + pub author: Option<CommitUser>, + pub committer: Option<CommitUser>, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub message: Option<String>, + pub parents: Option<Vec<CommitMeta>>, + pub sha: Option<String>, + pub tree: Option<CommitMeta>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// FileDeleteResponse contains information about a repo's file that was deleted +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct FileDeleteResponse { + pub commit: Option<FileCommitResponse>, + pub content: Option<serde_json::Value>, + pub verification: Option<PayloadCommitVerification>, +} + +/// FileLinksResponse contains the links for a repo's file +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct FileLinksResponse { + pub git: Option<String>, + pub html: Option<String>, + #[serde(rename = "self")] + pub this: Option<String>, +} + +/// FileResponse contains information about a repo's file +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct FileResponse { + pub commit: Option<FileCommitResponse>, + pub content: Option<ContentsResponse>, + pub verification: Option<PayloadCommitVerification>, +} + +/// FilesResponse contains information about multiple files from a repo +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct FilesResponse { + pub commit: Option<FileCommitResponse>, + pub files: Option<Vec<ContentsResponse>>, + pub verification: Option<PayloadCommitVerification>, +} + +/// GPGKey a user GPG key to sign commit and tag in repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GPGKey { + pub can_certify: Option<bool>, + pub can_encrypt_comms: Option<bool>, + pub can_encrypt_storage: Option<bool>, + pub can_sign: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub emails: Option<Vec<GPGKeyEmail>>, + #[serde(with = "time::serde::rfc3339::option")] + pub expires_at: Option<time::OffsetDateTime>, + pub id: Option<u64>, + pub key_id: Option<String>, + pub primary_key_id: Option<String>, + pub public_key: Option<String>, + pub subkeys: Option<Vec<GPGKey>>, + pub verified: Option<bool>, +} + +/// GPGKeyEmail an email attached to a GPGKey +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GPGKeyEmail { + pub email: Option<String>, + pub verified: Option<bool>, +} + +/// GeneralAPISettings contains global api settings exposed by it +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GeneralAPISettings { + pub default_git_trees_per_page: Option<u64>, + pub default_max_blob_size: Option<u64>, + pub default_paging_num: Option<u64>, + pub max_response_items: Option<u64>, +} + +/// GeneralAttachmentSettings contains global Attachment settings exposed by API +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GeneralAttachmentSettings { + pub allowed_types: Option<String>, + pub enabled: Option<bool>, + pub max_files: Option<u64>, + pub max_size: Option<u64>, +} + +/// GeneralRepoSettings contains global repository settings exposed by API +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GeneralRepoSettings { + pub http_git_disabled: Option<bool>, + pub lfs_disabled: Option<bool>, + pub migrations_disabled: Option<bool>, + pub mirrors_disabled: Option<bool>, + pub stars_disabled: Option<bool>, + pub time_tracking_disabled: Option<bool>, +} + +/// GeneralUISettings contains global ui settings exposed by API +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GeneralUISettings { + pub allowed_reactions: Option<Vec<String>>, + pub custom_emojis: Option<Vec<String>>, + pub default_theme: Option<String>, +} + +/// GenerateRepoOption options when creating repository using a template +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GenerateRepoOption { + /// include avatar of the template repo + pub avatar: Option<bool>, + /// Default branch of the new repository + pub default_branch: Option<String>, + /// Description of the repository to create + pub description: Option<String>, + /// include git content of default branch in template repo + pub git_content: Option<bool>, + /// include git hooks in template repo + pub git_hooks: Option<bool>, + /// include labels in template repo + pub labels: Option<bool>, + /// Name of the repository to create + pub name: String, + /// The organization or person who will own the new repository + pub owner: String, + /// Whether the repository is private + pub private: Option<bool>, + /// include protected branches in template repo + pub protected_branch: Option<bool>, + /// include topics in template repo + pub topics: Option<bool>, + /// include webhooks in template repo + pub webhooks: Option<bool>, +} + +/// GitBlobResponse represents a git blob +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GitBlobResponse { + pub content: Option<String>, + pub encoding: Option<String>, + pub sha: Option<String>, + pub size: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// GitEntry represents a git tree +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GitEntry { + pub mode: Option<String>, + pub path: Option<String>, + pub sha: Option<String>, + pub size: Option<u64>, + #[serde(rename = "type")] + pub r#type: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// GitHook represents a Git repository hook +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GitHook { + pub content: Option<String>, + pub is_active: Option<bool>, + pub name: Option<String>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GitObject { + pub sha: Option<String>, + #[serde(rename = "type")] + pub r#type: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// GitTreeResponse returns a git tree +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GitTreeResponse { + pub page: Option<u64>, + pub sha: Option<String>, + pub total_count: Option<u64>, + pub tree: Option<Vec<GitEntry>>, + pub truncated: Option<bool>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// GitignoreTemplateInfo name and text of a gitignore template +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GitignoreTemplateInfo { + pub name: Option<String>, + pub source: Option<String>, +} + +/// Hook a hook is a web hook when one repository changed +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Hook { + pub active: Option<bool>, + pub authorization_header: Option<String>, + pub branch_filter: Option<String>, + pub config: Option<BTreeMap<String, String>>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub events: Option<Vec<String>>, + pub id: Option<u64>, + #[serde(rename = "type")] + pub r#type: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// Identity for a person's identity like an author or committer +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Identity { + pub email: Option<String>, + pub name: Option<String>, +} + +/// InternalTracker represents settings for internal tracker +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct InternalTracker { + /// Let only contributors track time (Built-in issue tracker) + pub allow_only_contributors_to_track_time: Option<bool>, + /// Enable dependencies for issues and pull requests (Built-in issue tracker) + pub enable_issue_dependencies: Option<bool>, + /// Enable time tracking (Built-in issue tracker) + pub enable_time_tracker: Option<bool>, +} + +/// Issue represents an issue in a repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Issue { + pub assets: Option<Vec<Attachment>>, + pub assignee: Option<User>, + pub assignees: Option<Vec<User>>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub closed_at: Option<time::OffsetDateTime>, + pub comments: Option<u64>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_date: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + pub is_locked: Option<bool>, + pub labels: Option<Vec<Label>>, + pub milestone: Option<Milestone>, + pub number: Option<u64>, + pub original_author: Option<String>, + pub original_author_id: Option<u64>, + pub pin_order: Option<u64>, + pub pull_request: Option<PullRequestMeta>, + #[serde(rename = "ref")] + pub r#ref: Option<String>, + pub repository: Option<RepositoryMeta>, + pub state: Option<String>, + pub title: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + pub user: Option<User>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueConfig { + pub blank_issues_enabled: Option<bool>, + pub contact_links: Option<Vec<IssueConfigContactLink>>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueConfigContactLink { + pub about: Option<String>, + pub name: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueConfigValidation { + pub message: Option<String>, + pub valid: Option<bool>, +} + +/// IssueDeadline represents an issue deadline +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueDeadline { + #[serde(with = "time::serde::rfc3339::option")] + pub due_date: Option<time::OffsetDateTime>, +} + +/// IssueFormField represents a form field +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueFormField { + pub attributes: Option<BTreeMap<String, serde_json::Value>>, + pub id: Option<String>, + #[serde(rename = "type")] + pub r#type: Option<String>, + pub validations: Option<BTreeMap<String, serde_json::Value>>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueFormFieldType {} + +/// IssueLabelsOption a collection of labels +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueLabelsOption { + /// list of label IDs + pub labels: Option<Vec<u64>>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// IssueMeta basic issue information +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueMeta { + pub index: Option<u64>, + pub owner: Option<String>, + pub repo: Option<String>, +} + +/// IssueTemplate represents an issue template for a repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct IssueTemplate { + pub about: Option<String>, + pub body: Option<Vec<IssueFormField>>, + pub content: Option<String>, + pub file_name: Option<String>, + pub labels: Option<Vec<String>>, + pub name: Option<String>, + #[serde(rename = "ref")] + pub r#ref: Option<String>, + pub title: Option<String>, +} + +/// Label a label to an issue or a pr +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Label { + pub color: Option<String>, + pub description: Option<String>, + pub exclusive: Option<bool>, + pub id: Option<u64>, + pub is_archived: Option<bool>, + pub name: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// LabelTemplate info of a Label template +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct LabelTemplate { + pub color: Option<String>, + pub description: Option<String>, + pub exclusive: Option<bool>, + pub name: Option<String>, +} + +/// LicensesInfo contains information about a License +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct LicenseTemplateInfo { + pub body: Option<String>, + pub implementation: Option<String>, + pub key: Option<String>, + pub name: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// LicensesListEntry is used for the API +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct LicensesTemplateListEntry { + pub key: Option<String>, + pub name: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// MarkdownOption markdown options +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct MarkdownOption { + /// Context to render + /// + /// + /// + /// in: body + #[serde(rename = "Context")] + pub context: Option<String>, + /// Mode to render (comment, gfm, markdown) + /// + /// + /// + /// in: body + #[serde(rename = "Mode")] + pub mode: Option<String>, + /// Text markdown to render + /// + /// + /// + /// in: body + #[serde(rename = "Text")] + pub text: Option<String>, + /// Is it a wiki page ? + /// + /// + /// + /// in: body + #[serde(rename = "Wiki")] + pub wiki: Option<bool>, +} + +/// MarkupOption markup options +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct MarkupOption { + /// Context to render + /// + /// + /// + /// in: body + #[serde(rename = "Context")] + pub context: Option<String>, + /// File path for detecting extension in file mode + /// + /// + /// + /// in: body + #[serde(rename = "FilePath")] + pub file_path: Option<String>, + /// Mode to render (comment, gfm, markdown, file) + /// + /// + /// + /// in: body + #[serde(rename = "Mode")] + pub mode: Option<String>, + /// Text markup to render + /// + /// + /// + /// in: body + #[serde(rename = "Text")] + pub text: Option<String>, + /// Is it a wiki page ? + /// + /// + /// + /// in: body + #[serde(rename = "Wiki")] + pub wiki: Option<bool>, +} + +/// MergePullRequestForm form for merging Pull Request +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct MergePullRequestOption { + #[serde(rename = "Do")] + pub r#do: MergePullRequestOptionDo, + #[serde(rename = "MergeCommitID")] + pub merge_commit_id: Option<String>, + #[serde(rename = "MergeMessageField")] + pub merge_message_field: Option<String>, + #[serde(rename = "MergeTitleField")] + pub merge_title_field: Option<String>, + pub delete_branch_after_merge: Option<bool>, + pub force_merge: Option<bool>, + pub head_commit_id: Option<String>, + pub merge_when_checks_succeed: Option<bool>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum MergePullRequestOptionDo { + #[serde(rename = "merge")] + Merge, + #[serde(rename = "rebase")] + Rebase, + #[serde(rename = "rebase-merge")] + RebaseMerge, + #[serde(rename = "squash")] + Squash, + #[serde(rename = "manually-merged")] + ManuallyMerged, +} +/// MigrateRepoOptions options for migrating repository's +/// +/// this is used to interact with api v1 +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct MigrateRepoOptions { + pub auth_password: Option<String>, + pub auth_token: Option<String>, + pub auth_username: Option<String>, + pub clone_addr: String, + pub description: Option<String>, + pub issues: Option<bool>, + pub labels: Option<bool>, + pub lfs: Option<bool>, + pub lfs_endpoint: Option<String>, + pub milestones: Option<bool>, + pub mirror: Option<bool>, + pub mirror_interval: Option<String>, + pub private: Option<bool>, + pub pull_requests: Option<bool>, + pub releases: Option<bool>, + pub repo_name: String, + /// Name of User or Organisation who will own Repo after migration + pub repo_owner: Option<String>, + pub service: Option<MigrateRepoOptionsService>, + /// deprecated (only for backwards compatibility) + pub uid: Option<u64>, + pub wiki: Option<bool>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum MigrateRepoOptionsService { + #[serde(rename = "git")] + Git, + #[serde(rename = "github")] + Github, + #[serde(rename = "gitea")] + Gitea, + #[serde(rename = "gitlab")] + Gitlab, + #[serde(rename = "gogs")] + Gogs, + #[serde(rename = "onedev")] + Onedev, + #[serde(rename = "gitbucket")] + Gitbucket, + #[serde(rename = "codebase")] + Codebase, +} +/// Milestone milestone is a collection of issues on one repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Milestone { + #[serde(with = "time::serde::rfc3339::option")] + pub closed_at: Option<time::OffsetDateTime>, + pub closed_issues: Option<u64>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub description: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_on: Option<time::OffsetDateTime>, + pub id: Option<u64>, + pub open_issues: Option<u64>, + pub state: Option<String>, + pub title: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, +} + +/// NewIssuePinsAllowed represents an API response that says if new Issue Pins are allowed +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NewIssuePinsAllowed { + pub issues: Option<bool>, + pub pull_requests: Option<bool>, +} + +/// NodeInfo contains standardized way of exposing metadata about a server running one of the distributed social networks +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NodeInfo { + pub metadata: Option<BTreeMap<String, serde_json::Value>>, + #[serde(rename = "openRegistrations")] + pub open_registrations: Option<bool>, + pub protocols: Option<Vec<String>>, + pub services: Option<NodeInfoServices>, + pub software: Option<NodeInfoSoftware>, + pub usage: Option<NodeInfoUsage>, + pub version: Option<String>, +} + +/// NodeInfoServices contains the third party sites this server can connect to via their application API +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NodeInfoServices { + pub inbound: Option<Vec<String>>, + pub outbound: Option<Vec<String>>, +} + +/// NodeInfoSoftware contains Metadata about server software in use +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NodeInfoSoftware { + pub homepage: Option<String>, + pub name: Option<String>, + pub repository: Option<String>, + pub version: Option<String>, +} + +/// NodeInfoUsage contains usage statistics for this server +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NodeInfoUsage { + #[serde(rename = "localComments")] + pub local_comments: Option<u64>, + #[serde(rename = "localPosts")] + pub local_posts: Option<u64>, + pub users: Option<NodeInfoUsageUsers>, +} + +/// NodeInfoUsageUsers contains statistics about the users of this server +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NodeInfoUsageUsers { + #[serde(rename = "activeHalfyear")] + pub active_halfyear: Option<u64>, + #[serde(rename = "activeMonth")] + pub active_month: Option<u64>, + pub total: Option<u64>, +} + +/// Note contains information related to a git note +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Note { + pub commit: Option<Commit>, + pub message: Option<String>, +} + +/// NotificationCount number of unread notifications +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NotificationCount { + pub new: Option<u64>, +} + +/// NotificationSubject contains the notification subject (Issue/Pull/Commit) +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NotificationSubject { + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub latest_comment_html_url: Option<url::Url>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub latest_comment_url: Option<url::Url>, + pub state: Option<String>, + pub title: Option<String>, + #[serde(rename = "type")] + pub r#type: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// NotificationThread expose Notification on API +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NotificationThread { + pub id: Option<u64>, + pub pinned: Option<bool>, + pub repository: Option<Repository>, + pub subject: Option<NotificationSubject>, + pub unread: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// NotifySubjectType represent type of notification subject +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct NotifySubjectType {} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct OAuth2Application { + pub client_id: Option<String>, + pub client_secret: Option<String>, + pub confidential_client: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + pub id: Option<u64>, + pub name: Option<String>, + pub redirect_uris: Option<Vec<String>>, +} + +/// Organization represents an organization +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Organization { + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub avatar_url: Option<url::Url>, + pub description: Option<String>, + pub email: Option<String>, + pub full_name: Option<String>, + pub id: Option<u64>, + pub location: Option<String>, + pub name: Option<String>, + pub repo_admin_change_team_access: Option<bool>, + /// deprecated + pub username: Option<String>, + pub visibility: Option<String>, + pub website: Option<String>, +} + +/// OrganizationPermissions list different users permissions on an organization +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct OrganizationPermissions { + pub can_create_repository: Option<bool>, + pub can_read: Option<bool>, + pub can_write: Option<bool>, + pub is_admin: Option<bool>, + pub is_owner: Option<bool>, +} + +/// PRBranchInfo information about a branch +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PRBranchInfo { + pub label: Option<String>, + #[serde(rename = "ref")] + pub r#ref: Option<String>, + pub repo: Option<Repository>, + pub repo_id: Option<u64>, + pub sha: Option<String>, +} + +/// Package represents a package +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Package { + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub creator: Option<User>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + pub name: Option<String>, + pub owner: Option<User>, + pub repository: Option<Repository>, + #[serde(rename = "type")] + pub r#type: Option<String>, + pub version: Option<String>, +} + +/// PackageFile represents a package file +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PackageFile { + #[serde(rename = "Size")] + pub size: Option<u64>, + pub id: Option<u64>, + pub md5: Option<String>, + pub name: Option<String>, + pub sha1: Option<String>, + pub sha256: Option<String>, + pub sha512: Option<String>, +} + +/// PayloadCommit represents a commit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PayloadCommit { + pub added: Option<Vec<String>>, + pub author: Option<PayloadUser>, + pub committer: Option<PayloadUser>, + /// sha1 hash of the commit + pub id: Option<String>, + pub message: Option<String>, + pub modified: Option<Vec<String>>, + pub removed: Option<Vec<String>>, + #[serde(with = "time::serde::rfc3339::option")] + pub timestamp: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + pub verification: Option<PayloadCommitVerification>, +} + +/// PayloadCommitVerification represents the GPG verification of a commit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PayloadCommitVerification { + pub payload: Option<String>, + pub reason: Option<String>, + pub signature: Option<String>, + pub signer: Option<PayloadUser>, + pub verified: Option<bool>, +} + +/// PayloadUser represents the author or committer of a commit +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PayloadUser { + pub email: Option<String>, + /// Full name of the commit author + pub name: Option<String>, + pub username: Option<String>, +} + +/// Permission represents a set of permissions +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Permission { + pub admin: Option<bool>, + pub pull: Option<bool>, + pub push: Option<bool>, +} + +/// PublicKey publickey is a user key to push code to repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PublicKey { + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub fingerprint: Option<String>, + pub id: Option<u64>, + pub key: Option<String>, + pub key_type: Option<String>, + pub read_only: Option<bool>, + pub title: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + pub user: Option<User>, +} + +/// PullRequest represents a pull request +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PullRequest { + pub allow_maintainer_edit: Option<bool>, + pub assignee: Option<User>, + pub assignees: Option<Vec<User>>, + pub base: Option<PRBranchInfo>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub closed_at: Option<time::OffsetDateTime>, + pub comments: Option<u64>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub diff_url: Option<url::Url>, + #[serde(with = "time::serde::rfc3339::option")] + pub due_date: Option<time::OffsetDateTime>, + pub head: Option<PRBranchInfo>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + pub is_locked: Option<bool>, + pub labels: Option<Vec<Label>>, + pub merge_base: Option<String>, + pub merge_commit_sha: Option<String>, + pub mergeable: Option<bool>, + pub merged: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub merged_at: Option<time::OffsetDateTime>, + pub merged_by: Option<User>, + pub milestone: Option<Milestone>, + pub number: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub patch_url: Option<url::Url>, + pub pin_order: Option<u64>, + pub requested_reviewers: Option<Vec<User>>, + pub state: Option<String>, + pub title: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + pub user: Option<User>, +} + +/// PullRequestMeta PR info if an issue is a PR +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PullRequestMeta { + pub merged: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub merged_at: Option<time::OffsetDateTime>, +} + +/// PullReview represents a pull request review +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PullReview { + pub body: Option<String>, + pub comments_count: Option<u64>, + pub commit_id: Option<String>, + pub dismissed: Option<bool>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + pub official: Option<bool>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub pull_request_url: Option<url::Url>, + pub stale: Option<bool>, + pub state: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub submitted_at: Option<time::OffsetDateTime>, + pub team: Option<Team>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + pub user: Option<User>, +} + +/// PullReviewComment represents a comment on a pull request review +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PullReviewComment { + pub body: Option<String>, + pub commit_id: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub diff_hunk: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + pub original_commit_id: Option<String>, + pub original_position: Option<u32>, + pub path: Option<String>, + pub position: Option<u32>, + pub pull_request_review_id: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub pull_request_url: Option<url::Url>, + pub resolver: Option<User>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + pub user: Option<User>, +} + +/// PullReviewRequestOptions are options to add or remove pull review requests +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PullReviewRequestOptions { + pub reviewers: Option<Vec<String>>, + pub team_reviewers: Option<Vec<String>>, +} + +/// PushMirror represents information of a push mirror +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct PushMirror { + pub created: Option<String>, + pub interval: Option<String>, + pub last_error: Option<String>, + pub last_update: Option<String>, + pub remote_address: Option<String>, + pub remote_name: Option<String>, + pub repo_name: Option<String>, + pub sync_on_commit: Option<bool>, +} + +/// Reaction contain one reaction +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Reaction { + pub content: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub user: Option<User>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Reference { + pub object: Option<GitObject>, + #[serde(rename = "ref")] + pub r#ref: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// Release represents a repository release +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Release { + pub assets: Option<Vec<Attachment>>, + pub author: Option<User>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub draft: Option<bool>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + pub name: Option<String>, + pub prerelease: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub published_at: Option<time::OffsetDateTime>, + pub tag_name: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub tarball_url: Option<url::Url>, + pub target_commitish: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub upload_url: Option<url::Url>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub zipball_url: Option<url::Url>, +} + +/// RenameUserOption options when renaming a user +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct RenameUserOption { + /// New username for this user. This name cannot be in use yet by any other user. + pub new_username: String, +} + +/// RepoCollaboratorPermission to get repository permission for a collaborator +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct RepoCollaboratorPermission { + pub permission: Option<String>, + pub role_name: Option<String>, + pub user: Option<User>, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct RepoCommit { + pub author: Option<CommitUser>, + pub committer: Option<CommitUser>, + pub message: Option<String>, + pub tree: Option<CommitMeta>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + pub verification: Option<PayloadCommitVerification>, +} + +/// RepoTopicOptions a collection of repo topic names +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct RepoTopicOptions { + /// list of topic names + pub topics: Option<Vec<String>>, +} + +/// RepoTransfer represents a pending repo transfer +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct RepoTransfer { + pub doer: Option<User>, + pub recipient: Option<User>, + pub teams: Option<Vec<Team>>, +} + +/// Repository represents a repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Repository { + pub allow_merge_commits: Option<bool>, + pub allow_rebase: Option<bool>, + pub allow_rebase_explicit: Option<bool>, + pub allow_rebase_update: Option<bool>, + pub allow_squash_merge: Option<bool>, + pub archived: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub archived_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub avatar_url: Option<url::Url>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub clone_url: Option<url::Url>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub default_allow_maintainer_edit: Option<bool>, + pub default_branch: Option<String>, + pub default_delete_branch_after_merge: Option<bool>, + pub default_merge_style: Option<String>, + pub description: Option<String>, + pub empty: Option<bool>, + pub external_tracker: Option<ExternalTracker>, + pub external_wiki: Option<ExternalWiki>, + pub fork: Option<bool>, + pub forks_count: Option<u64>, + pub full_name: Option<String>, + pub has_actions: Option<bool>, + pub has_issues: Option<bool>, + pub has_packages: Option<bool>, + pub has_projects: Option<bool>, + pub has_pull_requests: Option<bool>, + pub has_releases: Option<bool>, + pub has_wiki: Option<bool>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + pub ignore_whitespace_conflicts: Option<bool>, + pub internal: Option<bool>, + pub internal_tracker: Option<InternalTracker>, + pub language: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub languages_url: Option<url::Url>, + pub link: Option<String>, + pub mirror: Option<bool>, + pub mirror_interval: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub mirror_updated: Option<time::OffsetDateTime>, + pub name: Option<String>, + pub open_issues_count: Option<u64>, + pub open_pr_counter: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub original_url: Option<url::Url>, + pub owner: Option<User>, + pub parent: Option<Box<Repository>>, + pub permissions: Option<Permission>, + pub private: Option<bool>, + pub release_counter: Option<u64>, + pub repo_transfer: Option<RepoTransfer>, + pub size: Option<u64>, + pub ssh_url: Option<String>, + pub stars_count: Option<u64>, + pub template: Option<bool>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, + pub watchers_count: Option<u64>, + pub website: Option<String>, +} + +/// RepositoryMeta basic repository information +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct RepositoryMeta { + pub full_name: Option<String>, + pub id: Option<u64>, + pub name: Option<String>, + pub owner: Option<String>, +} + +/// ReviewStateType review state type +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ReviewStateType {} + +/// SearchResults results of a successful search +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct SearchResults { + pub data: Option<Vec<Repository>>, + pub ok: Option<bool>, +} + +/// Secret represents a secret +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Secret { + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + /// the secret's name + pub name: Option<String>, +} + +/// ServerVersion wraps the version of the server +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct ServerVersion { + pub version: Option<String>, +} + +/// StateType issue state type +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct StateType {} + +/// StopWatch represent a running stopwatch +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct StopWatch { + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + pub duration: Option<String>, + pub issue_index: Option<u64>, + pub issue_title: Option<String>, + pub repo_name: Option<String>, + pub repo_owner_name: Option<String>, + pub seconds: Option<u64>, +} + +/// SubmitPullReviewOptions are options to submit a pending pull review +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct SubmitPullReviewOptions { + pub body: Option<String>, + pub event: Option<String>, +} + +/// Tag represents a repository tag +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Tag { + pub commit: Option<CommitMeta>, + pub id: Option<String>, + pub message: Option<String>, + pub name: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub tarball_url: Option<url::Url>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub zipball_url: Option<url::Url>, +} + +/// Team represents a team in an organization +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Team { + pub can_create_org_repo: Option<bool>, + pub description: Option<String>, + pub id: Option<u64>, + pub includes_all_repositories: Option<bool>, + pub name: Option<String>, + pub organization: Option<Organization>, + pub permission: Option<TeamPermission>, + pub units: Option<Vec<String>>, + pub units_map: Option<BTreeMap<String, String>>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum TeamPermission { + #[serde(rename = "none")] + None, + #[serde(rename = "read")] + Read, + #[serde(rename = "write")] + Write, + #[serde(rename = "admin")] + Admin, + #[serde(rename = "owner")] + Owner, +} +/// TimeStamp defines a timestamp +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TimeStamp {} + +/// TimelineComment represents a timeline comment (comment of any type) on a commit or issue +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TimelineComment { + pub assignee: Option<User>, + pub assignee_team: Option<Team>, + pub body: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub dependent_issue: Option<Issue>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub id: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub issue_url: Option<url::Url>, + pub label: Option<Label>, + pub milestone: Option<Milestone>, + pub new_ref: Option<String>, + pub new_title: Option<String>, + pub old_milestone: Option<Milestone>, + pub old_project_id: Option<u64>, + pub old_ref: Option<String>, + pub old_title: Option<String>, + pub project_id: Option<u64>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub pull_request_url: Option<url::Url>, + pub ref_action: Option<String>, + pub ref_comment: Option<Comment>, + /// commit SHA where issue/PR was referenced + pub ref_commit_sha: Option<String>, + pub ref_issue: Option<Issue>, + /// whether the assignees were removed or added + pub removed_assignee: Option<bool>, + pub resolve_doer: Option<User>, + pub review_id: Option<u64>, + pub tracked_time: Option<TrackedTime>, + #[serde(rename = "type")] + pub r#type: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated_at: Option<time::OffsetDateTime>, + pub user: Option<User>, +} + +/// TopicName a list of repo topic names +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TopicName { + pub topics: Option<Vec<String>>, +} + +/// TopicResponse for returning topics +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TopicResponse { + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + pub id: Option<u64>, + pub repo_count: Option<u64>, + pub topic_name: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub updated: Option<time::OffsetDateTime>, +} + +/// TrackedTime worked time for an issue / pr +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TrackedTime { + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + pub id: Option<u64>, + pub issue: Option<Issue>, + /// deprecated (only for backwards compatibility) + pub issue_id: Option<u64>, + /// Time in seconds + pub time: Option<u64>, + /// deprecated (only for backwards compatibility) + pub user_id: Option<u64>, + pub user_name: Option<String>, +} + +/// TransferRepoOption options when transfer a repository's ownership +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TransferRepoOption { + pub new_owner: String, + /// ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories. + pub team_ids: Option<Vec<u64>>, +} + +/// UpdateFileOptions options for updating files +/// +/// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct UpdateFileOptions { + pub author: Option<Identity>, + /// branch (optional) to base this file from. if not given, the default branch is used + pub branch: Option<String>, + pub committer: Option<Identity>, + /// content must be base64 encoded + pub content: String, + pub dates: Option<CommitDateOptions>, + /// from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL + pub from_path: Option<String>, + /// message (optional) for the commit of this file. if not supplied, a default message will be used + pub message: Option<String>, + /// new_branch (optional) will make a new branch from `branch` before creating the file + pub new_branch: Option<String>, + /// sha is the SHA for the file that already exists + pub sha: String, + /// Add a Signed-off-by trailer by the committer at the end of the commit log message. + pub signoff: Option<bool>, +} + +/// UpdateRepoAvatarUserOption options when updating the repo avatar +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct UpdateRepoAvatarOption { + /// image must be base64 encoded + pub image: Option<String>, +} + +/// UpdateUserAvatarUserOption options when updating the user avatar +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct UpdateUserAvatarOption { + /// image must be base64 encoded + pub image: Option<String>, +} + +/// User represents a user +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct User { + /// Is user active + pub active: Option<bool>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + /// URL to the user's avatar + pub avatar_url: Option<url::Url>, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option<time::OffsetDateTime>, + /// the user's description + pub description: Option<String>, + pub email: Option<String>, + /// user counts + pub followers_count: Option<u64>, + pub following_count: Option<u64>, + /// the user's full name + pub full_name: Option<String>, + /// the user's id + pub id: Option<u64>, + /// Is the user an administrator + pub is_admin: Option<bool>, + /// User locale + pub language: Option<String>, + #[serde(with = "time::serde::rfc3339::option")] + pub last_login: Option<time::OffsetDateTime>, + /// the user's location + pub location: Option<String>, + /// the user's username + pub login: Option<String>, + /// the user's authentication sign-in name. + pub login_name: Option<String>, + /// Is user login prohibited + pub prohibit_login: Option<bool>, + /// Is user restricted + pub restricted: Option<bool>, + pub starred_repos_count: Option<u64>, + /// User visibility level option: public, limited, private + pub visibility: Option<String>, + /// the user's website + pub website: Option<String>, +} + +/// UserHeatmapData represents the data needed to create a heatmap +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct UserHeatmapData { + pub contributions: Option<u64>, + pub timestamp: Option<u64>, +} + +/// UserSettings represents user settings +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct UserSettings { + pub description: Option<String>, + pub diff_view_style: Option<String>, + pub full_name: Option<String>, + pub hide_activity: Option<bool>, + /// Privacy + pub hide_email: Option<bool>, + pub language: Option<String>, + pub location: Option<String>, + pub theme: Option<String>, + pub website: Option<String>, +} + +/// UserSettingsOptions represents options to change user settings +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct UserSettingsOptions { + pub description: Option<String>, + pub diff_view_style: Option<String>, + pub full_name: Option<String>, + pub hide_activity: Option<bool>, + /// Privacy + pub hide_email: Option<bool>, + pub language: Option<String>, + pub location: Option<String>, + pub theme: Option<String>, + pub website: Option<String>, +} + +/// WatchInfo represents an API watch status of one repository +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct WatchInfo { + #[serde(with = "time::serde::rfc3339::option")] + pub created_at: Option<time::OffsetDateTime>, + pub ignored: Option<bool>, + pub reason: Option<serde_json::Value>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub repository_url: Option<url::Url>, + pub subscribed: Option<bool>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub url: Option<url::Url>, +} + +/// WikiCommit page commit/revision +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct WikiCommit { + pub author: Option<CommitUser>, + pub commiter: Option<CommitUser>, + pub message: Option<String>, + pub sha: Option<String>, +} + +/// WikiCommitList commit/revision list +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct WikiCommitList { + pub commits: Option<Vec<WikiCommit>>, + pub count: Option<u64>, +} + +/// WikiPage a wiki page +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct WikiPage { + pub commit_count: Option<u64>, + /// Page content, base64 encoded + pub content_base64: Option<String>, + pub footer: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub last_commit: Option<WikiCommit>, + pub sidebar: Option<String>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub sub_url: Option<url::Url>, + pub title: Option<String>, +} + +/// WikiPageMetaData wiki page meta information +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct WikiPageMetaData { + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub html_url: Option<url::Url>, + pub last_commit: Option<WikiCommit>, + #[serde(deserialize_with = "crate::none_if_blank_url")] + pub sub_url: Option<url::Url>, + 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(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)?; + s.parse::<bool>() + .map_err(|_| StructureError::HeaderParseFailed) + }) + .transpose()?; + let x_page = map + .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 = map + .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 = map + .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 = map + .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(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)?; + s.parse::<bool>() + .map_err(|_| StructureError::HeaderParseFailed) + }) + .transpose()?; + let x_page = map + .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 = map + .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 = map + .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 = map + .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(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 = map + .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(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 = map + .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(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 = map + .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(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 = map + .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 { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for AdminCronListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct AdminGetAllEmailsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for AdminGetAllEmailsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct AdminSearchEmailsQuery { + /// keyword + pub q: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for AdminSearchEmailsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(q) = &self.q { + write!(f, "q={q}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct AdminListHooksQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for AdminListHooksQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct AdminGetAllOrgsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for AdminGetAllOrgsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct AdminUnadoptedListQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, + /// pattern of repositories to search for + pub pattern: Option<String>, +} + +impl std::fmt::Display for AdminUnadoptedListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + if let Some(pattern) = &self.pattern { + write!(f, "pattern={pattern}&")?; + } + + Ok(()) + } +} + +pub struct AdminSearchUsersQuery { + /// ID of the user's login source to search for + pub source_id: Option<u64>, + /// user's login name to search for + pub login_name: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for AdminSearchUsersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(source_id) = &self.source_id { + write!(f, "source_id={source_id}&")?; + } + if let Some(login_name) = &self.login_name { + write!(f, "login_name={login_name}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct AdminDeleteUserQuery { + /// purge the user from the system completely + pub purge: Option<bool>, +} + +impl std::fmt::Display for AdminDeleteUserQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(purge) = &self.purge { + write!(f, "purge={purge}&")?; + } + + Ok(()) + } +} + +pub struct NotifyGetListQuery { + /// If true, show notifications marked as read. Default value is false + pub all: Option<bool>, + /// Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned. + pub status_types: Option<Vec<String>>, + /// filter notifications by subject type + pub subject_type: Option<Vec<NotifyGetListQuerySubjectType>>, + /// Only show notifications updated after the given time. This is a timestamp in RFC 3339 format + pub since: Option<time::OffsetDateTime>, + /// Only show notifications updated before the given time. This is a timestamp in RFC 3339 format + pub before: Option<time::OffsetDateTime>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for NotifyGetListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(all) = &self.all { + write!(f, "all={all}&")?; + } + if let Some(status_types) = &self.status_types { + if !status_types.is_empty() { + for item in status_types { + write!(f, "status-types=")?; + write!(f, "{item}")?; + write!(f, "&")?; + } + } + } + if let Some(subject_type) = &self.subject_type { + if !subject_type.is_empty() { + for item in subject_type { + write!(f, "subject-type=")?; + write!(f, "{}", item.as_str())?; + write!(f, "&")?; + } + } + } + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum NotifyGetListQuerySubjectType { + #[serde(rename = "issue")] + Issue, + #[serde(rename = "pull")] + Pull, + #[serde(rename = "commit")] + Commit, + #[serde(rename = "repository")] + Repository, +} + +impl NotifyGetListQuerySubjectType { + fn as_str(&self) -> &'static str { + match self { + NotifyGetListQuerySubjectType::Issue => "issue", + NotifyGetListQuerySubjectType::Pull => "pull", + NotifyGetListQuerySubjectType::Commit => "commit", + NotifyGetListQuerySubjectType::Repository => "repository", + } + } +} +pub struct NotifyReadListQuery { + /// Describes the last point that notifications were checked. Anything updated since this time will not be updated. + pub last_read_at: Option<time::OffsetDateTime>, + /// If true, mark all notifications on this repo. Default value is false + pub all: Option<String>, + /// Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread. + pub status_types: Option<Vec<String>>, + /// Status to mark notifications as, Defaults to read. + pub to_status: Option<String>, +} + +impl std::fmt::Display for NotifyReadListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(last_read_at) = &self.last_read_at { + write!( + f, + "last_read_at={field_name}&", + field_name = last_read_at + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(all) = &self.all { + write!(f, "all={all}&")?; + } + if let Some(status_types) = &self.status_types { + if !status_types.is_empty() { + for item in status_types { + write!(f, "status-types=")?; + write!(f, "{item}")?; + write!(f, "&")?; + } + } + } + if let Some(to_status) = &self.to_status { + write!(f, "to-status={to_status}&")?; + } + + Ok(()) + } +} + +pub struct NotifyReadThreadQuery { + /// Status to mark notifications as + pub to_status: Option<String>, +} + +impl std::fmt::Display for NotifyReadThreadQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(to_status) = &self.to_status { + write!(f, "to-status={to_status}&")?; + } + + Ok(()) + } +} + +pub struct OrgGetAllQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgGetAllQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListActionsSecretsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListActionsSecretsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListActivityFeedsQuery { + /// the date of the activities to be found + pub date: Option<time::Date>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListActivityFeedsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(date) = &self.date { + write!( + f, + "date={field_name}&", + field_name = date + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListHooksQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListHooksQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListLabelsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListLabelsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListBlockedUsersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListBlockedUsersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListMembersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListMembersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListPublicMembersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListPublicMembersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListReposQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListReposQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListTeamsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListTeamsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct TeamSearchQuery { + /// keywords to search + pub q: Option<String>, + /// include search within team description (defaults to true) + pub include_desc: Option<bool>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for TeamSearchQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(q) = &self.q { + write!(f, "q={q}&")?; + } + if let Some(include_desc) = &self.include_desc { + write!(f, "include_desc={include_desc}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct TeamSearchResponse { + pub data: Option<Vec<Team>>, + pub ok: Option<bool>, +} + +pub struct ListPackagesQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, + /// package type filter + pub r#type: Option<ListPackagesQueryType>, + /// name filter + pub q: Option<String>, +} + +impl std::fmt::Display for ListPackagesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + if let Some(r#type) = &self.r#type { + write!(f, "type={}&", r#type.as_str())?; + } + if let Some(q) = &self.q { + write!(f, "q={q}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum ListPackagesQueryType { + #[serde(rename = "alpine")] + Alpine, + #[serde(rename = "cargo")] + Cargo, + #[serde(rename = "chef")] + Chef, + #[serde(rename = "composer")] + Composer, + #[serde(rename = "conan")] + Conan, + #[serde(rename = "conda")] + Conda, + #[serde(rename = "container")] + Container, + #[serde(rename = "cran")] + Cran, + #[serde(rename = "debian")] + Debian, + #[serde(rename = "generic")] + Generic, + #[serde(rename = "go")] + Go, + #[serde(rename = "helm")] + Helm, + #[serde(rename = "maven")] + Maven, + #[serde(rename = "npm")] + Npm, + #[serde(rename = "nuget")] + Nuget, + #[serde(rename = "pub")] + Pub, + #[serde(rename = "pypi")] + Pypi, + #[serde(rename = "rpm")] + Rpm, + #[serde(rename = "rubygems")] + Rubygems, + #[serde(rename = "swift")] + Swift, + #[serde(rename = "vagrant")] + Vagrant, +} + +impl ListPackagesQueryType { + fn as_str(&self) -> &'static str { + match self { + ListPackagesQueryType::Alpine => "alpine", + ListPackagesQueryType::Cargo => "cargo", + ListPackagesQueryType::Chef => "chef", + ListPackagesQueryType::Composer => "composer", + ListPackagesQueryType::Conan => "conan", + ListPackagesQueryType::Conda => "conda", + ListPackagesQueryType::Container => "container", + ListPackagesQueryType::Cran => "cran", + ListPackagesQueryType::Debian => "debian", + ListPackagesQueryType::Generic => "generic", + ListPackagesQueryType::Go => "go", + ListPackagesQueryType::Helm => "helm", + ListPackagesQueryType::Maven => "maven", + ListPackagesQueryType::Npm => "npm", + ListPackagesQueryType::Nuget => "nuget", + ListPackagesQueryType::Pub => "pub", + ListPackagesQueryType::Pypi => "pypi", + ListPackagesQueryType::Rpm => "rpm", + ListPackagesQueryType::Rubygems => "rubygems", + ListPackagesQueryType::Swift => "swift", + ListPackagesQueryType::Vagrant => "vagrant", + } + } +} +pub struct IssueSearchIssuesQuery { + /// whether issue is open or closed + pub state: Option<String>, + /// comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded + pub labels: Option<String>, + /// comma separated list of milestone names. Fetch only issues that have any of this milestones. Non existent are discarded + pub milestones: Option<String>, + /// search string + pub q: Option<String>, + /// repository to prioritize in the results + pub priority_repo_id: Option<u64>, + /// filter by type (issues / pulls) if set + pub r#type: Option<String>, + /// Only show notifications updated after the given time. This is a timestamp in RFC 3339 format + pub since: Option<time::OffsetDateTime>, + /// Only show notifications updated before the given time. This is a timestamp in RFC 3339 format + pub before: Option<time::OffsetDateTime>, + /// filter (issues / pulls) assigned to you, default is false + pub assigned: Option<bool>, + /// filter (issues / pulls) created by you, default is false + pub created: Option<bool>, + /// filter (issues / pulls) mentioning you, default is false + pub mentioned: Option<bool>, + /// filter pulls requesting your review, default is false + pub review_requested: Option<bool>, + /// filter pulls reviewed by you, default is false + pub reviewed: Option<bool>, + /// filter by owner + pub owner: Option<String>, + /// filter by team (requires organization owner parameter to be provided) + pub team: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueSearchIssuesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(state) = &self.state { + write!(f, "state={state}&")?; + } + if let Some(labels) = &self.labels { + write!(f, "labels={labels}&")?; + } + if let Some(milestones) = &self.milestones { + write!(f, "milestones={milestones}&")?; + } + if let Some(q) = &self.q { + write!(f, "q={q}&")?; + } + if let Some(priority_repo_id) = &self.priority_repo_id { + write!(f, "priority_repo_id={priority_repo_id}&")?; + } + if let Some(r#type) = &self.r#type { + write!(f, "type={type}&")?; + } + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(assigned) = &self.assigned { + write!(f, "assigned={assigned}&")?; + } + if let Some(created) = &self.created { + write!(f, "created={created}&")?; + } + if let Some(mentioned) = &self.mentioned { + write!(f, "mentioned={mentioned}&")?; + } + if let Some(review_requested) = &self.review_requested { + write!(f, "review_requested={review_requested}&")?; + } + if let Some(reviewed) = &self.reviewed { + write!(f, "reviewed={reviewed}&")?; + } + if let Some(owner) = &self.owner { + write!(f, "owner={owner}&")?; + } + if let Some(team) = &self.team { + write!(f, "team={team}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoSearchQuery { + /// keyword + pub q: Option<String>, + /// Limit search to repositories with keyword as topic + pub topic: Option<bool>, + /// include search of keyword within repository description + pub include_desc: Option<bool>, + /// search only for repos that the user with the given id owns or contributes to + pub uid: Option<u64>, + /// repo owner to prioritize in the results + pub priority_owner_id: Option<u64>, + /// search only for repos that belong to the given team id + pub team_id: Option<u64>, + /// search only for repos that the user with the given id has starred + pub starred_by: Option<u64>, + /// include private repositories this user has access to (defaults to true) + pub private: Option<bool>, + /// show only pubic, private or all repositories (defaults to all) + pub is_private: Option<bool>, + /// include template repositories this user has access to (defaults to true) + pub template: Option<bool>, + /// show only archived, non-archived or all repositories (defaults to all) + pub archived: Option<bool>, + /// type of repository to search for. Supported values are "fork", "source", "mirror" and "collaborative" + pub mode: Option<String>, + /// if `uid` is given, search only for repos that the user owns + pub exclusive: Option<bool>, + /// sort repos by attribute. Supported values are "alpha", "created", "updated", "size", and "id". Default is "alpha" + pub sort: Option<String>, + /// sort order, either "asc" (ascending) or "desc" (descending). Default is "asc", ignored if "sort" is not specified. + pub order: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoSearchQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(q) = &self.q { + write!(f, "q={q}&")?; + } + if let Some(topic) = &self.topic { + write!(f, "topic={topic}&")?; + } + if let Some(include_desc) = &self.include_desc { + write!(f, "includeDesc={include_desc}&")?; + } + if let Some(uid) = &self.uid { + write!(f, "uid={uid}&")?; + } + if let Some(priority_owner_id) = &self.priority_owner_id { + write!(f, "priority_owner_id={priority_owner_id}&")?; + } + if let Some(team_id) = &self.team_id { + write!(f, "team_id={team_id}&")?; + } + if let Some(starred_by) = &self.starred_by { + write!(f, "starredBy={starred_by}&")?; + } + if let Some(private) = &self.private { + write!(f, "private={private}&")?; + } + if let Some(is_private) = &self.is_private { + write!(f, "is_private={is_private}&")?; + } + if let Some(template) = &self.template { + write!(f, "template={template}&")?; + } + if let Some(archived) = &self.archived { + write!(f, "archived={archived}&")?; + } + if let Some(mode) = &self.mode { + write!(f, "mode={mode}&")?; + } + if let Some(exclusive) = &self.exclusive { + write!(f, "exclusive={exclusive}&")?; + } + if let Some(sort) = &self.sort { + write!(f, "sort={sort}&")?; + } + if let Some(order) = &self.order { + write!(f, "order={order}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListActivityFeedsQuery { + /// the date of the activities to be found + pub date: Option<time::Date>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListActivityFeedsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(date) = &self.date { + write!( + f, + "date={field_name}&", + field_name = date + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListBranchesQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListBranchesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListCollaboratorsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListCollaboratorsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetAllCommitsQuery { + /// SHA or branch to start listing commits from (usually 'master') + pub sha: Option<String>, + /// filepath of a file/dir + pub path: Option<String>, + /// include diff stats for every commit (disable for speedup, default 'true') + pub stat: Option<bool>, + /// include verification for every commit (disable for speedup, default 'true') + pub verification: Option<bool>, + /// include a list of affected files for every commit (disable for speedup, default 'true') + pub files: Option<bool>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results (ignored if used with 'path') + pub limit: Option<u32>, + /// commits that match the given specifier will not be listed. + pub not: Option<String>, +} + +impl std::fmt::Display for RepoGetAllCommitsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(sha) = &self.sha { + write!(f, "sha={sha}&")?; + } + if let Some(path) = &self.path { + write!(f, "path={path}&")?; + } + if let Some(stat) = &self.stat { + write!(f, "stat={stat}&")?; + } + if let Some(verification) = &self.verification { + write!(f, "verification={verification}&")?; + } + if let Some(files) = &self.files { + write!(f, "files={files}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + if let Some(not) = &self.not { + write!(f, "not={not}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetCombinedStatusByRefQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoGetCombinedStatusByRefQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListStatusesByRefQuery { + /// type of sort + pub sort: Option<RepoListStatusesByRefQuerySort>, + /// type of state + pub state: Option<RepoListStatusesByRefQueryState>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListStatusesByRefQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(sort) = &self.sort { + write!(f, "sort={}&", sort.as_str())?; + } + if let Some(state) = &self.state { + write!(f, "state={}&", state.as_str())?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoListStatusesByRefQuerySort { + #[serde(rename = "oldest")] + Oldest, + #[serde(rename = "recentupdate")] + Recentupdate, + #[serde(rename = "leastupdate")] + Leastupdate, + #[serde(rename = "leastindex")] + Leastindex, + #[serde(rename = "highestindex")] + Highestindex, +} + +impl RepoListStatusesByRefQuerySort { + fn as_str(&self) -> &'static str { + match self { + RepoListStatusesByRefQuerySort::Oldest => "oldest", + RepoListStatusesByRefQuerySort::Recentupdate => "recentupdate", + RepoListStatusesByRefQuerySort::Leastupdate => "leastupdate", + RepoListStatusesByRefQuerySort::Leastindex => "leastindex", + RepoListStatusesByRefQuerySort::Highestindex => "highestindex", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoListStatusesByRefQueryState { + #[serde(rename = "pending")] + Pending, + #[serde(rename = "success")] + Success, + #[serde(rename = "error")] + Error, + #[serde(rename = "failure")] + Failure, + #[serde(rename = "warning")] + Warning, +} + +impl RepoListStatusesByRefQueryState { + fn as_str(&self) -> &'static str { + match self { + RepoListStatusesByRefQueryState::Pending => "pending", + RepoListStatusesByRefQueryState::Success => "success", + RepoListStatusesByRefQueryState::Error => "error", + RepoListStatusesByRefQueryState::Failure => "failure", + RepoListStatusesByRefQueryState::Warning => "warning", + } + } +} +pub struct RepoGetContentsListQuery { + /// The name of the commit/branch/tag. Default the repository’s default branch (usually master) + pub r#ref: Option<String>, +} + +impl std::fmt::Display for RepoGetContentsListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#ref) = &self.r#ref { + write!(f, "ref={ref}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetContentsQuery { + /// The name of the commit/branch/tag. Default the repository’s default branch (usually master) + pub r#ref: Option<String>, +} + +impl std::fmt::Display for RepoGetContentsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#ref) = &self.r#ref { + write!(f, "ref={ref}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetEditorConfigQuery { + /// The name of the commit/branch/tag. Default the repository’s default branch (usually master) + pub r#ref: Option<String>, +} + +impl std::fmt::Display for RepoGetEditorConfigQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#ref) = &self.r#ref { + write!(f, "ref={ref}&")?; + } + + Ok(()) + } +} + +pub struct ListForksQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for ListForksQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetSingleCommitQuery { + /// include diff stats for every commit (disable for speedup, default 'true') + pub stat: Option<bool>, + /// include verification for every commit (disable for speedup, default 'true') + pub verification: Option<bool>, + /// include a list of affected files for every commit (disable for speedup, default 'true') + pub files: Option<bool>, +} + +impl std::fmt::Display for RepoGetSingleCommitQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(stat) = &self.stat { + write!(f, "stat={stat}&")?; + } + if let Some(verification) = &self.verification { + write!(f, "verification={verification}&")?; + } + if let Some(files) = &self.files { + write!(f, "files={files}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetNoteQuery { + /// include verification for every commit (disable for speedup, default 'true') + pub verification: Option<bool>, + /// include a list of affected files for every commit (disable for speedup, default 'true') + pub files: Option<bool>, +} + +impl std::fmt::Display for RepoGetNoteQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(verification) = &self.verification { + write!(f, "verification={verification}&")?; + } + if let Some(files) = &self.files { + write!(f, "files={files}&")?; + } + + Ok(()) + } +} + +pub struct GetTreeQuery { + /// show all directories and files + pub recursive: Option<bool>, + /// page number; the 'truncated' field in the response will be true if there are still more items after this page, false if the last page + pub page: Option<u32>, + /// number of items per page + pub per_page: Option<u32>, +} + +impl std::fmt::Display for GetTreeQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(recursive) = &self.recursive { + write!(f, "recursive={recursive}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(per_page) = &self.per_page { + write!(f, "per_page={per_page}&")?; + } + + Ok(()) + } +} + +pub struct RepoListHooksQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListHooksQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoTestHookQuery { + /// The name of the commit/branch/tag, indicates which commit will be loaded to the webhook payload. + pub r#ref: Option<String>, +} + +impl std::fmt::Display for RepoTestHookQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#ref) = &self.r#ref { + write!(f, "ref={ref}&")?; + } + + Ok(()) + } +} + +pub struct IssueListIssuesQuery { + /// whether issue is open or closed + pub state: Option<IssueListIssuesQueryState>, + /// comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded + pub labels: Option<String>, + /// search string + pub q: Option<String>, + /// filter by type (issues / pulls) if set + pub r#type: Option<IssueListIssuesQueryType>, + /// comma separated list of milestone names or ids. It uses names and fall back to ids. Fetch only issues that have any of this milestones. Non existent milestones are discarded + pub milestones: Option<String>, + /// Only show items updated after the given time. This is a timestamp in RFC 3339 format + pub since: Option<time::OffsetDateTime>, + /// Only show items updated before the given time. This is a timestamp in RFC 3339 format + pub before: Option<time::OffsetDateTime>, + /// Only show items which were created by the the given user + pub created_by: Option<String>, + /// Only show items for which the given user is assigned + pub assigned_by: Option<String>, + /// Only show items in which the given user was mentioned + pub mentioned_by: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueListIssuesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(state) = &self.state { + write!(f, "state={}&", state.as_str())?; + } + if let Some(labels) = &self.labels { + write!(f, "labels={labels}&")?; + } + if let Some(q) = &self.q { + write!(f, "q={q}&")?; + } + if let Some(r#type) = &self.r#type { + write!(f, "type={}&", r#type.as_str())?; + } + if let Some(milestones) = &self.milestones { + write!(f, "milestones={milestones}&")?; + } + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(created_by) = &self.created_by { + write!(f, "created_by={created_by}&")?; + } + if let Some(assigned_by) = &self.assigned_by { + write!(f, "assigned_by={assigned_by}&")?; + } + if let Some(mentioned_by) = &self.mentioned_by { + write!(f, "mentioned_by={mentioned_by}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum IssueListIssuesQueryState { + #[serde(rename = "closed")] + Closed, + #[serde(rename = "open")] + Open, + #[serde(rename = "all")] + All, +} + +impl IssueListIssuesQueryState { + fn as_str(&self) -> &'static str { + match self { + IssueListIssuesQueryState::Closed => "closed", + IssueListIssuesQueryState::Open => "open", + IssueListIssuesQueryState::All => "all", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum IssueListIssuesQueryType { + #[serde(rename = "issues")] + Issues, + #[serde(rename = "pulls")] + Pulls, +} + +impl IssueListIssuesQueryType { + fn as_str(&self) -> &'static str { + match self { + IssueListIssuesQueryType::Issues => "issues", + IssueListIssuesQueryType::Pulls => "pulls", + } + } +} +pub struct IssueGetRepoCommentsQuery { + /// if provided, only comments updated since the provided time are returned. + pub since: Option<time::OffsetDateTime>, + /// if provided, only comments updated before the provided time are returned. + pub before: Option<time::OffsetDateTime>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueGetRepoCommentsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct IssueCreateIssueCommentAttachmentQuery { + /// name of the attachment + pub name: Option<String>, + /// time of the attachment's creation. This is a timestamp in RFC 3339 format + pub updated_at: Option<time::OffsetDateTime>, +} + +impl std::fmt::Display for IssueCreateIssueCommentAttachmentQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(name) = &self.name { + write!(f, "name={name}&")?; + } + if let Some(updated_at) = &self.updated_at { + write!( + f, + "updated_at={field_name}&", + field_name = updated_at + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + + Ok(()) + } +} + +pub struct IssueCreateIssueAttachmentQuery { + /// name of the attachment + pub name: Option<String>, + /// time of the attachment's creation. This is a timestamp in RFC 3339 format + pub updated_at: Option<time::OffsetDateTime>, +} + +impl std::fmt::Display for IssueCreateIssueAttachmentQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(name) = &self.name { + write!(f, "name={name}&")?; + } + if let Some(updated_at) = &self.updated_at { + write!( + f, + "updated_at={field_name}&", + field_name = updated_at + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + + Ok(()) + } +} + +pub struct IssueListBlocksQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueListBlocksQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct IssueGetCommentsQuery { + /// if provided, only comments updated since the specified time are returned. + pub since: Option<time::OffsetDateTime>, + /// if provided, only comments updated before the provided time are returned. + pub before: Option<time::OffsetDateTime>, +} + +impl std::fmt::Display for IssueGetCommentsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + + Ok(()) + } +} + +pub struct IssueListIssueDependenciesQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueListIssueDependenciesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct IssueGetIssueReactionsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueGetIssueReactionsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct IssueSubscriptionsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueSubscriptionsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct IssueGetCommentsAndTimelineQuery { + /// if provided, only comments updated since the specified time are returned. + pub since: Option<time::OffsetDateTime>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, + /// if provided, only comments updated before the provided time are returned. + pub before: Option<time::OffsetDateTime>, +} + +impl std::fmt::Display for IssueGetCommentsAndTimelineQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + + Ok(()) + } +} + +pub struct IssueTrackedTimesQuery { + /// optional filter by user (available for issue managers) + pub user: Option<String>, + /// Only show times updated after the given time. This is a timestamp in RFC 3339 format + pub since: Option<time::OffsetDateTime>, + /// Only show times updated before the given time. This is a timestamp in RFC 3339 format + pub before: Option<time::OffsetDateTime>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueTrackedTimesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(user) = &self.user { + write!(f, "user={user}&")?; + } + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListKeysQuery { + /// the key_id to search for + pub key_id: Option<u32>, + /// fingerprint of the key + pub fingerprint: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListKeysQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(key_id) = &self.key_id { + write!(f, "key_id={key_id}&")?; + } + if let Some(fingerprint) = &self.fingerprint { + write!(f, "fingerprint={fingerprint}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct IssueListLabelsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueListLabelsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetRawFileOrLfsQuery { + /// The name of the commit/branch/tag. Default the repository’s default branch (usually master) + pub r#ref: Option<String>, +} + +impl std::fmt::Display for RepoGetRawFileOrLfsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#ref) = &self.r#ref { + write!(f, "ref={ref}&")?; + } + + Ok(()) + } +} + +pub struct IssueGetMilestonesListQuery { + /// Milestone state, Recognized values are open, closed and all. Defaults to "open" + pub state: Option<String>, + /// filter by milestone name + pub name: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for IssueGetMilestonesListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(state) = &self.state { + write!(f, "state={state}&")?; + } + if let Some(name) = &self.name { + write!(f, "name={name}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct NotifyGetRepoListQuery { + /// If true, show notifications marked as read. Default value is false + pub all: Option<bool>, + /// Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned + pub status_types: Option<Vec<String>>, + /// filter notifications by subject type + pub subject_type: Option<Vec<NotifyGetRepoListQuerySubjectType>>, + /// Only show notifications updated after the given time. This is a timestamp in RFC 3339 format + pub since: Option<time::OffsetDateTime>, + /// Only show notifications updated before the given time. This is a timestamp in RFC 3339 format + pub before: Option<time::OffsetDateTime>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for NotifyGetRepoListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(all) = &self.all { + write!(f, "all={all}&")?; + } + if let Some(status_types) = &self.status_types { + if !status_types.is_empty() { + for item in status_types { + write!(f, "status-types=")?; + write!(f, "{item}")?; + write!(f, "&")?; + } + } + } + if let Some(subject_type) = &self.subject_type { + if !subject_type.is_empty() { + for item in subject_type { + write!(f, "subject-type=")?; + write!(f, "{}", item.as_str())?; + write!(f, "&")?; + } + } + } + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum NotifyGetRepoListQuerySubjectType { + #[serde(rename = "issue")] + Issue, + #[serde(rename = "pull")] + Pull, + #[serde(rename = "commit")] + Commit, + #[serde(rename = "repository")] + Repository, +} + +impl NotifyGetRepoListQuerySubjectType { + fn as_str(&self) -> &'static str { + match self { + NotifyGetRepoListQuerySubjectType::Issue => "issue", + NotifyGetRepoListQuerySubjectType::Pull => "pull", + NotifyGetRepoListQuerySubjectType::Commit => "commit", + NotifyGetRepoListQuerySubjectType::Repository => "repository", + } + } +} +pub struct NotifyReadRepoListQuery { + /// If true, mark all notifications on this repo. Default value is false + pub all: Option<String>, + /// Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread. + pub status_types: Option<Vec<String>>, + /// Status to mark notifications as. Defaults to read. + pub to_status: Option<String>, + /// Describes the last point that notifications were checked. Anything updated since this time will not be updated. + pub last_read_at: Option<time::OffsetDateTime>, +} + +impl std::fmt::Display for NotifyReadRepoListQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(all) = &self.all { + write!(f, "all={all}&")?; + } + if let Some(status_types) = &self.status_types { + if !status_types.is_empty() { + for item in status_types { + write!(f, "status-types=")?; + write!(f, "{item}")?; + write!(f, "&")?; + } + } + } + if let Some(to_status) = &self.to_status { + write!(f, "to-status={to_status}&")?; + } + if let Some(last_read_at) = &self.last_read_at { + write!( + f, + "last_read_at={field_name}&", + field_name = last_read_at + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + + Ok(()) + } +} + +pub struct RepoListPullRequestsQuery { + /// State of pull request: open or closed (optional) + pub state: Option<RepoListPullRequestsQueryState>, + /// Type of sort + pub sort: Option<RepoListPullRequestsQuerySort>, + /// ID of the milestone + pub milestone: Option<u64>, + /// Label IDs + pub labels: Option<Vec<u64>>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListPullRequestsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(state) = &self.state { + write!(f, "state={}&", state.as_str())?; + } + if let Some(sort) = &self.sort { + write!(f, "sort={}&", sort.as_str())?; + } + if let Some(milestone) = &self.milestone { + write!(f, "milestone={milestone}&")?; + } + if let Some(labels) = &self.labels { + if !labels.is_empty() { + for item in labels { + write!(f, "labels=")?; + write!(f, "{item}")?; + write!(f, "&")?; + } + } + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoListPullRequestsQueryState { + #[serde(rename = "closed")] + Closed, + #[serde(rename = "open")] + Open, + #[serde(rename = "all")] + All, +} + +impl RepoListPullRequestsQueryState { + fn as_str(&self) -> &'static str { + match self { + RepoListPullRequestsQueryState::Closed => "closed", + RepoListPullRequestsQueryState::Open => "open", + RepoListPullRequestsQueryState::All => "all", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoListPullRequestsQuerySort { + #[serde(rename = "oldest")] + Oldest, + #[serde(rename = "recentupdate")] + Recentupdate, + #[serde(rename = "leastupdate")] + Leastupdate, + #[serde(rename = "mostcomment")] + Mostcomment, + #[serde(rename = "leastcomment")] + Leastcomment, + #[serde(rename = "priority")] + Priority, +} + +impl RepoListPullRequestsQuerySort { + fn as_str(&self) -> &'static str { + match self { + RepoListPullRequestsQuerySort::Oldest => "oldest", + RepoListPullRequestsQuerySort::Recentupdate => "recentupdate", + RepoListPullRequestsQuerySort::Leastupdate => "leastupdate", + RepoListPullRequestsQuerySort::Mostcomment => "mostcomment", + RepoListPullRequestsQuerySort::Leastcomment => "leastcomment", + RepoListPullRequestsQuerySort::Priority => "priority", + } + } +} +pub struct RepoDownloadPullDiffOrPatchQuery { + /// whether to include binary file changes. if true, the diff is applicable with `git apply` + pub binary: Option<bool>, +} + +impl std::fmt::Display for RepoDownloadPullDiffOrPatchQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(binary) = &self.binary { + write!(f, "binary={binary}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetPullRequestCommitsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, + /// include verification for every commit (disable for speedup, default 'true') + pub verification: Option<bool>, + /// include a list of affected files for every commit (disable for speedup, default 'true') + pub files: Option<bool>, +} + +impl std::fmt::Display for RepoGetPullRequestCommitsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + if let Some(verification) = &self.verification { + write!(f, "verification={verification}&")?; + } + if let Some(files) = &self.files { + write!(f, "files={files}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetPullRequestFilesQuery { + /// skip to given file + pub skip_to: Option<String>, + /// whitespace behavior + pub whitespace: Option<RepoGetPullRequestFilesQueryWhitespace>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoGetPullRequestFilesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(skip_to) = &self.skip_to { + write!(f, "skip-to={skip_to}&")?; + } + if let Some(whitespace) = &self.whitespace { + write!(f, "whitespace={}&", whitespace.as_str())?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoGetPullRequestFilesQueryWhitespace { + #[serde(rename = "ignore-all")] + IgnoreAll, + #[serde(rename = "ignore-change")] + IgnoreChange, + #[serde(rename = "ignore-eol")] + IgnoreEol, + #[serde(rename = "show-all")] + ShowAll, +} + +impl RepoGetPullRequestFilesQueryWhitespace { + fn as_str(&self) -> &'static str { + match self { + RepoGetPullRequestFilesQueryWhitespace::IgnoreAll => "ignore-all", + RepoGetPullRequestFilesQueryWhitespace::IgnoreChange => "ignore-change", + RepoGetPullRequestFilesQueryWhitespace::IgnoreEol => "ignore-eol", + RepoGetPullRequestFilesQueryWhitespace::ShowAll => "show-all", + } + } +} +pub struct RepoListPullReviewsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListPullReviewsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoUpdatePullRequestQuery { + /// how to update pull request + pub style: Option<RepoUpdatePullRequestQueryStyle>, +} + +impl std::fmt::Display for RepoUpdatePullRequestQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(style) = &self.style { + write!(f, "style={}&", style.as_str())?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoUpdatePullRequestQueryStyle { + #[serde(rename = "merge")] + Merge, + #[serde(rename = "rebase")] + Rebase, +} + +impl RepoUpdatePullRequestQueryStyle { + fn as_str(&self) -> &'static str { + match self { + RepoUpdatePullRequestQueryStyle::Merge => "merge", + RepoUpdatePullRequestQueryStyle::Rebase => "rebase", + } + } +} +pub struct RepoListPushMirrorsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListPushMirrorsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetRawFileQuery { + /// The name of the commit/branch/tag. Default the repository’s default branch (usually master) + pub r#ref: Option<String>, +} + +impl std::fmt::Display for RepoGetRawFileQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(r#ref) = &self.r#ref { + write!(f, "ref={ref}&")?; + } + + Ok(()) + } +} + +pub struct RepoListReleasesQuery { + /// filter (exclude / include) drafts, if you dont have repo write access none will show + pub draft: Option<bool>, + /// filter (exclude / include) pre-releases + pub pre_release: Option<bool>, + /// page size of results, deprecated - use limit + pub per_page: Option<u32>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListReleasesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(draft) = &self.draft { + write!(f, "draft={draft}&")?; + } + if let Some(pre_release) = &self.pre_release { + write!(f, "pre-release={pre_release}&")?; + } + if let Some(per_page) = &self.per_page { + write!(f, "per_page={per_page}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoCreateReleaseAttachmentQuery { + /// name of the attachment + pub name: Option<String>, +} + +impl std::fmt::Display for RepoCreateReleaseAttachmentQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(name) = &self.name { + write!(f, "name={name}&")?; + } + + Ok(()) + } +} + +pub struct RepoListStargazersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListStargazersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListStatusesQuery { + /// type of sort + pub sort: Option<RepoListStatusesQuerySort>, + /// type of state + pub state: Option<RepoListStatusesQueryState>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListStatusesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(sort) = &self.sort { + write!(f, "sort={}&", sort.as_str())?; + } + if let Some(state) = &self.state { + write!(f, "state={}&", state.as_str())?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoListStatusesQuerySort { + #[serde(rename = "oldest")] + Oldest, + #[serde(rename = "recentupdate")] + Recentupdate, + #[serde(rename = "leastupdate")] + Leastupdate, + #[serde(rename = "leastindex")] + Leastindex, + #[serde(rename = "highestindex")] + Highestindex, +} + +impl RepoListStatusesQuerySort { + fn as_str(&self) -> &'static str { + match self { + RepoListStatusesQuerySort::Oldest => "oldest", + RepoListStatusesQuerySort::Recentupdate => "recentupdate", + RepoListStatusesQuerySort::Leastupdate => "leastupdate", + RepoListStatusesQuerySort::Leastindex => "leastindex", + RepoListStatusesQuerySort::Highestindex => "highestindex", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum RepoListStatusesQueryState { + #[serde(rename = "pending")] + Pending, + #[serde(rename = "success")] + Success, + #[serde(rename = "error")] + Error, + #[serde(rename = "failure")] + Failure, + #[serde(rename = "warning")] + Warning, +} + +impl RepoListStatusesQueryState { + fn as_str(&self) -> &'static str { + match self { + RepoListStatusesQueryState::Pending => "pending", + RepoListStatusesQueryState::Success => "success", + RepoListStatusesQueryState::Error => "error", + RepoListStatusesQueryState::Failure => "failure", + RepoListStatusesQueryState::Warning => "warning", + } + } +} +pub struct RepoListSubscribersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListSubscribersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListTagsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results, default maximum page size is 50 + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListTagsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoTrackedTimesQuery { + /// optional filter by user (available for issue managers) + pub user: Option<String>, + /// Only show times updated after the given time. This is a timestamp in RFC 3339 format + pub since: Option<time::OffsetDateTime>, + /// Only show times updated before the given time. This is a timestamp in RFC 3339 format + pub before: Option<time::OffsetDateTime>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoTrackedTimesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(user) = &self.user { + write!(f, "user={user}&")?; + } + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoListTopicsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoListTopicsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetWikiPagesQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for RepoGetWikiPagesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct RepoGetWikiPageRevisionsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, +} + +impl std::fmt::Display for RepoGetWikiPageRevisionsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + + Ok(()) + } +} + +pub struct OrgListTeamActivityFeedsQuery { + /// the date of the activities to be found + pub date: Option<time::Date>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListTeamActivityFeedsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(date) = &self.date { + write!( + f, + "date={field_name}&", + field_name = date + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListTeamMembersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListTeamMembersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListTeamReposQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListTeamReposQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct TopicSearchQuery { + /// keywords to search + pub q: String, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for TopicSearchQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let q = &self.q; + write!(f, "q={q}&")?; + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserGetOAuth2ApplicationsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserGetOAuth2ApplicationsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentListFollowersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserCurrentListFollowersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentListFollowingQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserCurrentListFollowingQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentListGpgKeysQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserCurrentListGpgKeysQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListHooksQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListHooksQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentListKeysQuery { + /// fingerprint of the key + pub fingerprint: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserCurrentListKeysQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(fingerprint) = &self.fingerprint { + write!(f, "fingerprint={fingerprint}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListBlockedUsersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListBlockedUsersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListCurrentUserOrgsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListCurrentUserOrgsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentListReposQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserCurrentListReposQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentListStarredQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserCurrentListStarredQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserGetStopWatchesQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserGetStopWatchesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentListSubscriptionsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserCurrentListSubscriptionsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListTeamsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListTeamsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserCurrentTrackedTimesQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, + /// Only show times updated after the given time. This is a timestamp in RFC 3339 format + pub since: Option<time::OffsetDateTime>, + /// Only show times updated before the given time. This is a timestamp in RFC 3339 format + pub before: Option<time::OffsetDateTime>, +} + +impl std::fmt::Display for UserCurrentTrackedTimesQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + if let Some(since) = &self.since { + write!( + f, + "since={field_name}&", + field_name = since + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(before) = &self.before { + write!( + f, + "before={field_name}&", + field_name = before + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + + Ok(()) + } +} + +pub struct UserSearchQuery { + /// keyword + pub q: Option<String>, + /// ID of the user to search for + pub uid: Option<u64>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserSearchQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(q) = &self.q { + write!(f, "q={q}&")?; + } + if let Some(uid) = &self.uid { + write!(f, "uid={uid}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct UserSearchResponse { + pub data: Option<Vec<User>>, + pub ok: Option<bool>, +} + +pub struct UserListActivityFeedsQuery { + /// if true, only show actions performed by the requested user + pub only_performed_by: Option<bool>, + /// the date of the activities to be found + pub date: Option<time::Date>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListActivityFeedsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(only_performed_by) = &self.only_performed_by { + write!(f, "only-performed-by={only_performed_by}&")?; + } + if let Some(date) = &self.date { + write!( + f, + "date={field_name}&", + field_name = date + .format(&time::format_description::well_known::Rfc3339) + .unwrap() + )?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListFollowersQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListFollowersQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListFollowingQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListFollowingQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListGpgKeysQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListGpgKeysQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListKeysQuery { + /// fingerprint of the key + pub fingerprint: Option<String>, + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListKeysQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(fingerprint) = &self.fingerprint { + write!(f, "fingerprint={fingerprint}&")?; + } + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct OrgListUserOrgsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for OrgListUserOrgsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListReposQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListReposQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListStarredQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListStarredQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserListSubscriptionsQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserListSubscriptionsQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} + +pub struct UserGetTokensQuery { + /// page number of results to return (1-based) + pub page: Option<u32>, + /// page size of results + pub limit: Option<u32>, +} + +impl std::fmt::Display for UserGetTokensQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(page) = &self.page { + write!(f, "page={page}&")?; + } + if let Some(limit) = &self.limit { + write!(f, "limit={limit}&")?; + } + + Ok(()) + } +} diff --git a/src/issue.rs b/src/issue.rs deleted file mode 100644 index 71f2fd5..0000000 --- a/src/issue.rs +++ /dev/null @@ -1,347 +0,0 @@ -use super::*; - -impl Forgejo { - pub async fn get_repo_issues( - &self, - owner: &str, - repo: &str, - query: IssueQuery, - ) -> Result<Vec<Issue>, ForgejoError> { - self.get(&query.to_string(owner, repo)).await - } - - pub async fn create_issue( - &self, - owner: &str, - repo: &str, - opts: CreateIssueOption, - ) -> Result<Issue, ForgejoError> { - self.post(&format!("repos/{owner}/{repo}/issues"), &opts) - .await - } - - pub async fn get_issue( - &self, - owner: &str, - repo: &str, - id: u64, - ) -> Result<Option<Issue>, ForgejoError> { - self.get_opt(&format!("repos/{owner}/{repo}/issues/{id}")) - .await - } - - pub async fn delete_issue(&self, owner: &str, repo: &str, id: u64) -> Result<(), ForgejoError> { - self.delete(&format!("repos/{owner}/{repo}/issues/{id}")) - .await - } - - pub async fn edit_issue( - &self, - owner: &str, - repo: &str, - id: u64, - opts: EditIssueOption, - ) -> Result<Issue, ForgejoError> { - self.patch(&format!("repos/{owner}/{repo}/issues/{id}"), &opts) - .await - } - - pub async fn get_repo_comments( - &self, - owner: &str, - repo: &str, - query: RepoCommentQuery, - ) -> Result<Vec<Comment>, ForgejoError> { - self.get(&query.to_string(owner, repo)).await - } - - pub async fn get_issue_comments( - &self, - owner: &str, - repo: &str, - issue_id: u64, - query: IssueCommentQuery, - ) -> Result<Vec<Comment>, ForgejoError> { - self.get(&query.to_string(owner, repo, issue_id)).await - } - - pub async fn create_comment( - &self, - owner: &str, - repo: &str, - issue_id: u64, - opts: CreateIssueCommentOption, - ) -> Result<Comment, ForgejoError> { - self.post( - &format!("repos/{owner}/{repo}/issues/{issue_id}/comments"), - &opts, - ) - .await - } - - pub async fn get_comment( - &self, - owner: &str, - repo: &str, - id: u64, - ) -> Result<Option<Comment>, ForgejoError> { - self.get_opt(&format!("repos/{owner}/{repo}/issues/comments/{id}")) - .await - } - - pub async fn delete_comment( - &self, - owner: &str, - repo: &str, - id: u64, - ) -> Result<(), ForgejoError> { - self.delete(&format!("repos/{owner}/{repo}/issues/comments/{id}")) - .await - } - - pub async fn edit_comment( - &self, - owner: &str, - repo: &str, - id: u64, - opts: EditIssueCommentOption, - ) -> Result<Comment, ForgejoError> { - self.patch(&format!("repos/{owner}/{repo}/issues/comments/{id}"), &opts) - .await - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Issue { - pub assets: Vec<Attachment>, - pub assignee: Option<User>, - pub assignees: Option<Vec<User>>, - pub body: String, - #[serde(with = "time::serde::rfc3339::option")] - pub closed_at: Option<time::OffsetDateTime>, - pub comments: u64, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - #[serde(with = "time::serde::rfc3339::option")] - pub due_date: Option<time::OffsetDateTime>, - pub html_url: Url, - pub id: u64, - pub is_locked: bool, - pub labels: Vec<Label>, - pub milestone: Option<Milestone>, - pub number: u64, - pub original_author: String, - pub original_author_id: u64, - pub pin_order: u64, - pub pull_request: Option<PullRequestMeta>, - #[serde(rename = "ref")] - pub _ref: String, - pub repository: RepositoryMeta, - pub state: State, - pub title: String, - #[serde(with = "time::serde::rfc3339")] - pub updated_at: time::OffsetDateTime, - pub url: Url, - pub user: User, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Label { - pub color: String, - pub description: String, - pub exclusive: bool, - pub id: u64, - pub name: String, - pub url: Url, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Attachment { - pub browser_download_url: Url, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub download_count: u64, - pub id: u64, - pub name: String, - pub size: u64, - pub uuid: String, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct EditAttachmentOption { - pub name: Option<String>, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone, Copy)] -pub enum State { - #[serde(rename = "open")] - Open, - #[serde(rename = "closed")] - Closed, -} - -impl State { - pub(crate) fn as_str(&self) -> &'static str { - match self { - State::Open => "open", - State::Closed => "closed", - } - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Comment { - pub assets: Vec<Attachment>, - pub body: String, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub html_url: Url, - pub id: u64, - pub issue_url: Url, - pub original_author: String, - pub original_author_id: u64, - #[serde(deserialize_with = "crate::none_if_blank_url")] - pub pull_request_url: Option<Url>, - #[serde(with = "time::serde::rfc3339")] - pub updated_at: time::OffsetDateTime, - pub user: User, -} - -#[derive(Default, Debug)] -pub struct IssueQuery { - pub state: Option<State>, - pub labels: Vec<String>, - pub query: Option<String>, - pub _type: Option<IssueQueryType>, - pub milestones: Vec<String>, - pub since: Option<time::OffsetDateTime>, - pub before: Option<time::OffsetDateTime>, - pub created_by: Option<String>, - pub assigned_by: Option<String>, - pub mentioned_by: Option<String>, - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl IssueQuery { - fn to_string(&self, owner: &str, repo: &str) -> String { - format!("repos/{owner}/{repo}/issues?state={}&labels={}&q={}&type={}&milestones={}&since={}&before={}&created_by={}&assigned_by={}&mentioned_by={}&page={}&limit={}", - self.state.map(|s| s.as_str()).unwrap_or_default(), - self.labels.join(","), - self.query.as_deref().unwrap_or_default(), - self._type.map(|t| t.as_str()).unwrap_or_default(), - self.milestones.join(","), - self.since.map(|t| t.format(&time::format_description::well_known::Rfc3339).unwrap()).unwrap_or_default(), - self.before.map(|t| t.format(&time::format_description::well_known::Rfc3339).unwrap()).unwrap_or_default(), - self.created_by.as_deref().unwrap_or_default(), - self.assigned_by.as_deref().unwrap_or_default(), - self.mentioned_by.as_deref().unwrap_or_default(), - self.page.map(|page| page.to_string()).unwrap_or_default(), - self.limit.map(|page| page.to_string()).unwrap_or_default(), - ) - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum IssueQueryType { - Issues, - Pulls, -} - -impl IssueQueryType { - fn as_str(&self) -> &'static str { - match self { - IssueQueryType::Issues => "issues", - IssueQueryType::Pulls => "pulls", - } - } -} - -#[derive(Default, Debug)] -pub struct IssueCommentQuery { - pub since: Option<time::OffsetDateTime>, - pub before: Option<time::OffsetDateTime>, -} - -impl IssueCommentQuery { - fn to_string(&self, owner: &str, repo: &str, issue_id: u64) -> String { - format!( - "repos/{owner}/{repo}/issues/{issue_id}/comments?since={}&before={}", - self.since - .map(|t| t - .format(&time::format_description::well_known::Rfc3339) - .unwrap()) - .unwrap_or_default(), - self.before - .map(|t| t - .format(&time::format_description::well_known::Rfc3339) - .unwrap()) - .unwrap_or_default(), - ) - } -} - -#[derive(Default, Debug)] -pub struct RepoCommentQuery { - pub since: Option<time::OffsetDateTime>, - pub before: Option<time::OffsetDateTime>, - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl RepoCommentQuery { - fn to_string(&self, owner: &str, repo: &str) -> String { - format!( - "repos/{owner}/{repo}/issues/comments?since={}&before={}&page={}&limit={}", - self.since - .map(|t| t - .format(&time::format_description::well_known::Rfc3339) - .unwrap()) - .unwrap_or_default(), - self.before - .map(|t| t - .format(&time::format_description::well_known::Rfc3339) - .unwrap()) - .unwrap_or_default(), - self.page.map(|page| page.to_string()).unwrap_or_default(), - self.limit.map(|page| page.to_string()).unwrap_or_default(), - ) - } -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct CreateIssueOption { - pub assignees: Vec<String>, - pub body: Option<String>, - pub closed: Option<bool>, - #[serde(with = "time::serde::rfc3339::option")] - pub due_date: Option<time::OffsetDateTime>, - pub labels: Vec<u64>, - pub milestone: Option<u64>, - pub _ref: Option<String>, - pub title: String, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct EditIssueOption { - pub assignees: Vec<String>, - pub body: Option<String>, - #[serde(with = "time::serde::rfc3339::option")] - pub due_date: Option<time::OffsetDateTime>, - pub labels: Vec<u64>, - pub milestone: Option<u64>, - pub _ref: Option<String>, - pub state: Option<State>, - pub title: Option<String>, - pub unset_due_date: Option<bool>, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct CreateIssueCommentOption { - pub body: String, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct EditIssueCommentOption { - pub body: String, -} @@ -1,5 +1,4 @@ use reqwest::{Client, Request, StatusCode}; -use serde::{de::DeserializeOwned, Serialize}; use soft_assert::*; use url::Url; use zeroize::Zeroize; @@ -9,23 +8,9 @@ pub struct Forgejo { client: Client, } -mod admin; -mod issue; -mod misc; -mod notification; -mod organization; -mod package; -mod repository; -mod user; - -pub use admin::*; -pub use issue::*; -pub use misc::*; -pub use notification::*; -pub use organization::*; -pub use package::*; -pub use repository::*; -pub use user::*; +mod generated; + +pub use generated::structs; #[derive(thiserror::Error, Debug)] pub enum ForgejoError { @@ -38,15 +23,30 @@ 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)] - ApiError(StatusCode, String), + #[error("{} {}{}", .0.as_u16(), .0.canonical_reason().unwrap_or(""), .1.as_ref().map(|s| format!(": {s}")).unwrap_or_default())] + ApiError(StatusCode, Option<String>), #[error("the provided authorization was too long to accept")] 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 @@ -134,218 +134,67 @@ impl Forgejo { Ok(Self { url, client }) } - async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T, ForgejoError> { - let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.get(url).build()?; - self.execute(request).await - } - - async fn get_opt<T: DeserializeOwned>(&self, path: &str) -> Result<Option<T>, ForgejoError> { - let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.get(url).build()?; - self.execute_opt(request).await - } - - async fn get_str(&self, path: &str) -> Result<String, ForgejoError> { - let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.get(url).build()?; - self.execute_str(request).await - } - - async fn get_exists(&self, path: &str) -> Result<bool, ForgejoError> { - let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.get(url).build()?; - self.execute_exists(request).await - } - - async fn post<T: Serialize, U: DeserializeOwned>( - &self, - path: &str, - body: &T, - ) -> Result<U, ForgejoError> { - let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.post(url).json(body).build()?; - self.execute(request).await - } - - async fn post_multipart<T: DeserializeOwned>( + pub async fn download_release_attachment( &self, - path: &str, - body: reqwest::multipart::Form, - ) -> Result<T, ForgejoError> { - let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.post(url).multipart(body).build()?; - self.execute(request).await - } - - async fn post_str_out<T: Serialize>( - &self, - path: &str, - body: &T, - ) -> Result<String, ForgejoError> { - let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.post(url).json(body).build()?; - self.execute_str(request).await + owner: &str, + repo: &str, + release: u64, + attach: u64, + ) -> Result<bytes::Bytes, ForgejoError> { + let release = self + .repo_get_release_attachment(owner, repo, release, attach) + .await?; + let request = self + .client + .get(format!("/attachments/{}", release.uuid.unwrap())) + .build()?; + Ok(self.execute(request).await?.bytes().await?) } - async fn post_unit<T: Serialize>(&self, path: &str, body: &T) -> Result<(), ForgejoError> { + fn get(&self, path: &str) -> reqwest::RequestBuilder { let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.post(url).json(body).build()?; - self.execute_unit(request).await + self.client.get(url) } - async fn post_raw(&self, path: &str, body: String) -> Result<String, ForgejoError> { + fn put(&self, path: &str) -> reqwest::RequestBuilder { let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.post(url).body(body).build()?; - self.execute_str(request).await + self.client.put(url) } - async fn delete(&self, path: &str) -> Result<(), ForgejoError> { + fn post(&self, path: &str) -> reqwest::RequestBuilder { let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.delete(url).build()?; - self.execute_unit(request).await + self.client.post(url) } - async fn patch<T: Serialize, U: DeserializeOwned>( - &self, - path: &str, - body: &T, - ) -> Result<U, ForgejoError> { + fn delete(&self, path: &str) -> reqwest::RequestBuilder { let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.patch(url).json(body).build()?; - self.execute(request).await + self.client.delete(url) } - async fn put<T: DeserializeOwned>(&self, path: &str) -> Result<T, ForgejoError> { + fn patch(&self, path: &str) -> reqwest::RequestBuilder { let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); - let request = self.client.put(url).build()?; - self.execute(request).await + self.client.patch(url) } - async fn execute<T: DeserializeOwned>(&self, request: Request) -> Result<T, ForgejoError> { + async fn execute(&self, request: Request) -> Result<reqwest::Response, ForgejoError> { let response = self.client.execute(request).await?; match response.status() { - status if status.is_success() => { - let body = response.text().await?; - let out = - serde_json::from_str(&body).map_err(|e| ForgejoError::BadStructure(e, body))?; - Ok(out) + status if status.is_success() => Ok(response), + status if status.is_client_error() => { + Err(ForgejoError::ApiError(status, maybe_err(response).await)) } - status if status.is_client_error() => Err(ForgejoError::ApiError( - status, - response - .json::<ErrorMessage>() - .await? - .message - .unwrap_or_else(|| String::from("[no message]")), - )), - status => Err(ForgejoError::UnexpectedStatusCode(status)), - } - } - - /// Like `execute`, but returns a `String`. - async fn execute_opt_raw( - &self, - request: Request, - ) -> Result<Option<bytes::Bytes>, ForgejoError> { - let response = self.client.execute(request).await?; - match response.status() { - status if status.is_success() => Ok(Some(response.bytes().await?)), - StatusCode::NOT_FOUND => Ok(None), - status if status.is_client_error() => Err(ForgejoError::ApiError( - status, - response - .json::<ErrorMessage>() - .await? - .message - .unwrap_or_else(|| String::from("[no message]")), - )), - status => Err(ForgejoError::UnexpectedStatusCode(status)), - } - } - - /// Like `execute`, but returns a `String`. - async fn execute_str(&self, request: Request) -> Result<String, ForgejoError> { - let response = self.client.execute(request).await?; - match response.status() { - status if status.is_success() => Ok(response.text().await?), - status if status.is_client_error() => Err(ForgejoError::ApiError( - status, - response - .json::<ErrorMessage>() - .await? - .message - .unwrap_or_else(|| String::from("[no message]")), - )), - status => Err(ForgejoError::UnexpectedStatusCode(status)), - } - } - - /// Like `execute`, but returns unit. - async fn execute_unit(&self, request: Request) -> Result<(), ForgejoError> { - let response = self.client.execute(request).await?; - match response.status() { - status if status.is_success() => Ok(()), - status if status.is_client_error() => Err(ForgejoError::ApiError( - status, - response - .json::<ErrorMessage>() - .await? - .message - .unwrap_or_else(|| String::from("[no message]")), - )), - status => Err(ForgejoError::UnexpectedStatusCode(status)), - } - } - - /// Like `execute`, but returns `Ok(None)` on 404. - async fn execute_opt<T: DeserializeOwned>( - &self, - request: Request, - ) -> Result<Option<T>, ForgejoError> { - let response = self.client.execute(request).await?; - match response.status() { - status if status.is_success() => { - let body = response.text().await?; - let out = - serde_json::from_str(&body).map_err(|e| ForgejoError::BadStructure(e, body))?; - Ok(out) - } - StatusCode::NOT_FOUND => Ok(None), - status if status.is_client_error() => Err(ForgejoError::ApiError( - status, - response - .json::<ErrorMessage>() - .await? - .message - .unwrap_or_else(|| String::from("[no message]")), - )), status => Err(ForgejoError::UnexpectedStatusCode(status)), } } +} - /// Like `execute`, but returns `false` on 404. - async fn execute_exists(&self, request: Request) -> Result<bool, ForgejoError> { - let response = self.client.execute(request).await?; - match response.status() { - status if status.is_success() => Ok(true), - StatusCode::NOT_FOUND => Ok(false), - status if status.is_client_error() => Err(ForgejoError::ApiError( - status, - response - .json::<ErrorMessage>() - .await? - .message - .unwrap_or_else(|| String::from("[no message]")), - )), - status => Err(ForgejoError::UnexpectedStatusCode(status)), - } - } +async fn maybe_err(res: reqwest::Response) -> Option<String> { + res.json::<ErrorMessage>().await.ok().map(|e| e.message) } #[derive(serde::Deserialize)] struct ErrorMessage { - message: Option<String>, + message: String, // intentionally ignored, no need for now // url: Url } diff --git a/src/misc.rs b/src/misc.rs deleted file mode 100644 index 76fc3b8..0000000 --- a/src/misc.rs +++ /dev/null @@ -1,162 +0,0 @@ -use super::*; - -impl Forgejo { - pub async fn get_gitignore_templates(&self) -> Result<Vec<String>, ForgejoError> { - self.get("gitignore/templates").await - } - - pub async fn get_gitignore_template( - &self, - name: &str, - ) -> Result<Option<GitignoreTemplateInfo>, ForgejoError> { - self.get_opt(&format!("gitignore/templates/{name}")).await - } - - pub async fn get_label_templates(&self) -> Result<Vec<String>, ForgejoError> { - self.get("label/templates").await - } - - pub async fn get_label_template(&self, name: &str) -> Result<Vec<LabelTemplate>, ForgejoError> { - self.get(&format!("label/templates/{name}")).await - } - - pub async fn get_licenses(&self) -> Result<Vec<LicenseTemplateListEntry>, ForgejoError> { - self.get("licenses").await - } - - pub async fn get_license( - &self, - name: &str, - ) -> Result<Option<GitignoreTemplateInfo>, ForgejoError> { - self.get_opt(&format!("license/{name}")).await - } - - pub async fn render_markdown(&self, opt: MarkdownOption) -> Result<String, ForgejoError> { - self.post_str_out("markdown", &opt).await - } - - pub async fn render_markdown_raw(&self, body: String) -> Result<String, ForgejoError> { - self.post_raw("markdown/raw", body).await - } - - pub async fn render_markup(&self, opt: MarkupOption) -> Result<String, ForgejoError> { - self.post_str_out("markup", &opt).await - } - - pub async fn nodeinfo(&self) -> Result<NodeInfo, ForgejoError> { - self.get("nodeinfo").await - } - - pub async fn signing_key(&self) -> Result<String, ForgejoError> { - self.get_str("signing-key.gpg").await - } - - pub async fn version(&self) -> Result<ServerVersion, ForgejoError> { - self.get("version").await - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct GitignoreTemplateInfo { - pub name: String, - pub source: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct LabelTemplate { - pub color: String, - pub description: String, - pub exclusive: bool, - pub name: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct LicenseTemplateListEntry { - pub key: String, - pub name: String, - pub url: Url, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct LicenseTemplateInfo { - pub body: String, - pub implementation: String, - pub key: String, - pub name: String, - pub url: Url, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct MarkdownOption { - #[serde(rename = "Context")] - pub context: String, - #[serde(rename = "Mode")] - pub mode: String, - #[serde(rename = "Text")] - pub text: String, - #[serde(rename = "Wiki")] - pub wiki: String, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct MarkupOption { - #[serde(rename = "Context")] - pub context: String, - #[serde(rename = "FilePath")] - pub file_path: String, - #[serde(rename = "Mode")] - pub mode: String, - #[serde(rename = "Text")] - pub text: String, - #[serde(rename = "Wiki")] - pub wiki: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NodeInfo { - pub metadata: std::collections::BTreeMap<String, String>, - #[serde(rename = "openRegistrations")] - pub open_registrations: bool, - pub protocols: Vec<String>, - pub services: NodeInfoServices, - pub software: NodeInfoSoftware, - pub usage: NodeInfoUsage, - pub version: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NodeInfoServices { - pub inbound: Vec<String>, - pub outbound: Vec<String>, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NodeInfoSoftware { - pub homepage: Url, - pub name: String, - pub repository: Url, - pub version: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NodeInfoUsage { - #[serde(rename = "localComments")] - pub local_comments: u64, - #[serde(rename = "localPosts")] - pub local_posts: u64, - pub users: NodeInfoUsageUsers, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NodeInfoUsageUsers { - #[serde(rename = "activeHalfYear")] - pub active_half_year: u64, - #[serde(rename = "activeMonth")] - pub active_month: u64, - pub total: u64, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct ServerVersion { - pub version: String, -} diff --git a/src/notification.rs b/src/notification.rs deleted file mode 100644 index 348363a..0000000 --- a/src/notification.rs +++ /dev/null @@ -1,273 +0,0 @@ -use super::*; - -impl Forgejo { - pub async fn notifications( - &self, - query: NotificationQuery, - ) -> Result<Vec<NotificationThread>, ForgejoError> { - self.get(&format!("notifications?{}", query.query_string())) - .await - } - - pub async fn set_notifications_state( - &self, - query: NotificationPutQuery, - ) -> Result<Vec<NotificationThread>, ForgejoError> { - self.put(&format!("notifications?{}", query.query_string())) - .await - } - - pub async fn notification_count(&self) -> Result<Vec<NotificationCount>, ForgejoError> { - self.get("notifications/new").await - } - - pub async fn get_notification( - &self, - id: u64, - ) -> Result<Option<NotificationThread>, ForgejoError> { - self.get_opt(&format!("notifications/threads/{id}")).await - } - - pub async fn set_notification_state( - &self, - id: u64, - to_status: ToStatus, - ) -> Result<Option<NotificationThread>, ForgejoError> { - self.patch( - &format!( - "notifications/threads/{id}?to-status={}", - to_status.as_str() - ), - &(), - ) - .await - } - - pub async fn get_repo_notifications( - &self, - owner: &str, - name: &str, - query: NotificationQuery, - ) -> Result<Vec<NotificationThread>, ForgejoError> { - self.get(&format!( - "repos/{owner}/{name}/notifications?{}", - query.query_string() - )) - .await - } - - pub async fn set_repo_notifications_state( - &self, - owner: &str, - name: &str, - query: NotificationPutQuery, - ) -> Result<Vec<NotificationThread>, ForgejoError> { - self.put(&format!( - "repos/{owner}/{name}/notifications?{}", - query.query_string() - )) - .await - } -} - -#[derive(Debug)] -pub struct NotificationQuery { - pub all: bool, - pub include_unread: bool, - pub include_read: bool, - pub include_pinned: bool, - pub subject_type: Option<NotificationSubjectType>, - pub since: Option<time::OffsetDateTime>, - pub before: Option<time::OffsetDateTime>, - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl Default for NotificationQuery { - fn default() -> Self { - NotificationQuery { - all: false, - include_unread: true, - include_read: false, - include_pinned: true, - subject_type: None, - since: None, - before: None, - page: None, - limit: None, - } - } -} - -impl NotificationQuery { - fn query_string(&self) -> String { - use std::fmt::Write; - let mut s = String::new(); - if self.all { - s.push_str("all=true&"); - } - if self.include_unread { - s.push_str("status-types=unread&"); - } - if self.include_read { - s.push_str("status-types=read&"); - } - if self.include_pinned { - s.push_str("status-types=pinned&"); - } - if let Some(subject_type) = self.subject_type { - s.push_str("subject-type="); - s.push_str(subject_type.as_str()); - s.push('&'); - } - if let Some(since) = &self.since { - s.push_str("since="); - s.push_str( - &since - .format(&time::format_description::well_known::Rfc3339) - .unwrap(), - ); - s.push('&'); - } - if let Some(before) = &self.before { - s.push_str("before="); - s.push_str( - &before - .format(&time::format_description::well_known::Rfc3339) - .unwrap(), - ); - s.push('&'); - } - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to a string never fails"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to a string never fails"); - } - s - } -} - -#[derive(Debug, Clone, Copy)] -pub enum NotificationSubjectType { - Issue, - Pull, - Commit, - Repository, -} - -impl NotificationSubjectType { - fn as_str(&self) -> &'static str { - match self { - NotificationSubjectType::Issue => "issue", - NotificationSubjectType::Pull => "pull", - NotificationSubjectType::Commit => "commit", - NotificationSubjectType::Repository => "repository", - } - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NotificationThread { - pub id: u64, - pub pinned: bool, - pub repository: Repository, - pub subject: NotificationSubject, - pub unread: bool, - #[serde(with = "time::serde::rfc3339")] - pub updated_at: time::OffsetDateTime, - pub url: Url, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NotificationSubject { - pub html_url: Url, - pub latest_comment_html_url: Url, - pub latest_comment_url: Url, - pub state: String, - pub title: String, - #[serde(rename = "type")] - pub _type: String, - pub url: Url, -} - -#[derive(Debug)] -pub struct NotificationPutQuery { - pub last_read_at: Option<time::OffsetDateTime>, - pub all: bool, - pub include_unread: bool, - pub include_read: bool, - pub include_pinned: bool, - pub to_status: ToStatus, -} - -impl Default for NotificationPutQuery { - fn default() -> Self { - NotificationPutQuery { - last_read_at: None, - all: false, - include_unread: true, - include_read: false, - include_pinned: false, - to_status: ToStatus::default(), - } - } -} - -impl NotificationPutQuery { - fn query_string(&self) -> String { - let mut s = String::new(); - if let Some(last_read_at) = &self.last_read_at { - s.push_str("since="); - s.push_str( - &last_read_at - .format(&time::format_description::well_known::Rfc3339) - .unwrap(), - ); - s.push('&'); - } - if self.all { - s.push_str("all=true&"); - } - if self.include_unread { - s.push_str("status-types=unread&"); - } - if self.include_read { - s.push_str("status-types=read&"); - } - if self.include_pinned { - s.push_str("status-types=pinned&"); - } - s.push_str("subject-type="); - s.push_str(self.to_status.as_str()); - s - } -} - -#[derive(Default, Debug)] -pub enum ToStatus { - #[default] - Read, - Unread, - Pinned, -} - -impl ToStatus { - fn as_str(&self) -> &'static str { - match self { - ToStatus::Read => "read", - ToStatus::Unread => "unread", - ToStatus::Pinned => "pinned", - } - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct NotificationCount { - pub new: u64, -} diff --git a/src/organization.rs b/src/organization.rs deleted file mode 100644 index 6a54f10..0000000 --- a/src/organization.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::*; -use std::collections::BTreeMap; - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Organization { - #[serde(deserialize_with = "crate::none_if_blank_url")] - pub avatar_url: Option<Url>, - pub description: String, - pub full_name: String, - pub id: u64, - pub location: Option<String>, - pub name: String, - pub repo_admin_change_team_access: bool, - pub visibility: String, - #[serde(deserialize_with = "crate::none_if_blank_url")] - pub website: Option<Url>, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Team { - pub can_create_org_repo: bool, - pub description: String, - pub id: u64, - pub includes_all_repositories: bool, - pub name: String, - pub organization: Organization, - pub permission: String, - pub units: Vec<String>, - pub units_map: BTreeMap<String, String>, -} diff --git a/src/package.rs b/src/package.rs deleted file mode 100644 index 4c49d5b..0000000 --- a/src/package.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::fmt::Write; - -use super::*; - -impl Forgejo { - pub async fn get_user_packages( - &self, - owner: &str, - query: PackagesQuery, - ) -> Result<Vec<Package>, ForgejoError> { - self.get(&query.path(owner)).await - } - - pub async fn get_package( - &self, - owner: &str, - _type: PackageType, - name: &str, - version: &str, - ) -> Result<Option<Package>, ForgejoError> { - self.get_opt(&format!( - "packages/{owner}/{}/{name}/{version}", - _type.as_str() - )) - .await - } - - pub async fn delete_package( - &self, - owner: &str, - _type: PackageType, - name: &str, - version: &str, - ) -> Result<(), ForgejoError> { - self.delete(&format!( - "packages/{owner}/{}/{name}/{version}", - _type.as_str() - )) - .await - } - - pub async fn get_package_files( - &self, - owner: &str, - _type: PackageType, - name: &str, - version: &str, - ) -> Result<Vec<PackageFile>, ForgejoError> { - self.get(&format!( - "packages/{owner}/{}/{name}/{version}", - _type.as_str() - )) - .await - } -} - -#[derive(Default, Debug)] -pub struct PackagesQuery { - pub page: Option<u32>, - pub limit: Option<u32>, - pub kind: Option<PackageType>, - pub query: String, -} - -impl PackagesQuery { - fn path(&self, owner: &str) -> String { - let mut s = String::from("packages/"); - s.push_str(owner); - s.push('?'); - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to string can't fail"); - s.push('&'); - } - if let Some(kind) = self.kind { - s.push_str("type="); - s.push_str(kind.as_str()); - s.push('&'); - } - if !self.query.is_empty() { - s.push_str("q="); - s.push_str(&self.query); - s.push('&'); - } - s - } -} - -#[derive(serde::Deserialize, Debug, PartialEq, Eq, Clone, Copy)] -#[serde(rename_all = "lowercase")] -#[non_exhaustive] -pub enum PackageType { - Alpine, - Cargo, - Chef, - Composer, - Conan, - Conda, - Container, - Cran, - Debian, - Generic, - Go, - Helm, - Maven, - Npm, - Nuget, - Pub, - Pypi, - Rpm, - RubyGems, - Swift, - Vagrant, -} - -impl PackageType { - fn as_str(&self) -> &'static str { - match self { - PackageType::Alpine => "alpine", - PackageType::Cargo => "cargo", - PackageType::Chef => "chef", - PackageType::Composer => "composer", - PackageType::Conan => "conan", - PackageType::Conda => "conda", - PackageType::Container => "container", - PackageType::Cran => "cran", - PackageType::Debian => "debian", - PackageType::Generic => "generic", - PackageType::Go => "go", - PackageType::Helm => "helm", - PackageType::Maven => "maven", - PackageType::Npm => "npm", - PackageType::Nuget => "nuget", - PackageType::Pub => "pub", - PackageType::Pypi => "pypi", - PackageType::Rpm => "rpm", - PackageType::RubyGems => "rubygems", - PackageType::Swift => "swift", - PackageType::Vagrant => "vagrant", - } - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Package { - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub creator: User, - pub id: u64, - pub name: String, - pub owner: User, - pub repository: Option<Repository>, - pub _type: PackageType, - pub version: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct PackageFile { - #[serde(rename = "Size")] - pub size: u64, - pub id: u64, - pub md5: String, - pub name: String, - pub sha1: String, - pub sha256: String, - pub sha512: String, -} diff --git a/src/repository.rs b/src/repository.rs deleted file mode 100644 index eb638ac..0000000 --- a/src/repository.rs +++ /dev/null @@ -1,743 +0,0 @@ -use super::*; - -/// Repository operations. -impl Forgejo { - /// Gets info about the specified repository. - pub async fn get_repo( - &self, - user: &str, - repo: &str, - ) -> Result<Option<Repository>, ForgejoError> { - self.get_opt(&format!("repos/{user}/{repo}/")).await - } - - /// Creates a repository. - pub async fn create_repo(&self, repo: CreateRepoOption) -> Result<Repository, ForgejoError> { - self.post("user/repos", &repo).await - } - - pub async fn get_pulls( - &self, - owner: &str, - repo: &str, - query: PullQuery, - ) -> Result<Vec<PullRequest>, ForgejoError> { - self.get(&query.to_string(owner, repo)).await - } - - pub async fn create_pr( - &self, - owner: &str, - repo: &str, - opts: CreatePullRequestOption, - ) -> Result<PullRequest, ForgejoError> { - self.post(&format!("repos/{owner}/{repo}/pulls"), &opts) - .await - } - - pub async fn is_merged(&self, owner: &str, repo: &str, pr: u64) -> Result<bool, ForgejoError> { - self.get_exists(&format!("repos/{owner}/{repo}/pulls/{pr}/merge")) - .await - } - - pub async fn merge_pr( - &self, - owner: &str, - repo: &str, - pr: u64, - opts: MergePullRequestOption, - ) -> Result<(), ForgejoError> { - self.post_unit(&format!("repos/{owner}/{repo}/pulls/{pr}/merge"), &opts) - .await - } - - pub async fn cancel_merge(&self, owner: &str, repo: &str, pr: u64) -> Result<(), ForgejoError> { - self.delete(&format!("repos/{owner}/{repo}/pulls/{pr}/merge")) - .await - } - - pub async fn get_releases( - &self, - owner: &str, - repo: &str, - query: ReleaseQuery, - ) -> Result<Vec<Release>, ForgejoError> { - self.get(&query.to_string(owner, repo)).await - } - - pub async fn get_release( - &self, - owner: &str, - repo: &str, - id: u64, - ) -> Result<Option<Release>, ForgejoError> { - self.get_opt(&format!("repos/{owner}/{repo}/releases/{id}")) - .await - } - - pub async fn get_release_by_tag( - &self, - owner: &str, - repo: &str, - tag: &str, - ) -> Result<Option<Release>, ForgejoError> { - self.get_opt(&format!("repos/{owner}/{repo}/releases/tags/{tag}")) - .await - } - - pub async fn delete_release( - &self, - owner: &str, - repo: &str, - id: u64, - ) -> Result<(), ForgejoError> { - self.delete(&format!("repos/{owner}/{repo}/releases/{id}")) - .await - } - - pub async fn delete_release_by_tag( - &self, - owner: &str, - repo: &str, - tag: &str, - ) -> Result<(), ForgejoError> { - self.delete(&format!("repos/{owner}/{repo}/releases/tags/{tag}")) - .await - } - - pub async fn edit_release( - &self, - owner: &str, - repo: &str, - id: u64, - opts: EditReleaseOption, - ) -> Result<Release, ForgejoError> { - self.patch(&format!("repos/{owner}/{repo}/releases/{id}"), &opts) - .await - } - - pub async fn get_release_attachments( - &self, - owner: &str, - repo: &str, - id: u64, - ) -> Result<Vec<Attachment>, ForgejoError> { - self.get(&format!("repos/{owner}/{repo}/releases/{id}/assets")) - .await - } - - pub async fn get_release_attachment( - &self, - owner: &str, - repo: &str, - release_id: u64, - attachment_id: u64, - ) -> Result<Attachment, ForgejoError> { - self.get(&format!( - "repos/{owner}/{repo}/releases/{release_id}/assets/{attachment_id}" - )) - .await - } - - pub async fn create_release_attachment( - &self, - owner: &str, - repo: &str, - id: u64, - name: &str, - file: Vec<u8>, - ) -> Result<Attachment, ForgejoError> { - self.post_multipart( - &format!("repos/{owner}/{repo}/releases/{id}/assets?name={name}"), - reqwest::multipart::Form::new().part( - "attachment", - reqwest::multipart::Part::bytes(file) - .file_name("file") - .mime_str("*/*") - .unwrap(), - ), - ) - .await - } - - pub async fn delete_release_attachment( - &self, - owner: &str, - repo: &str, - release_id: u64, - attachment_id: u64, - ) -> Result<(), ForgejoError> { - self.delete(&format!( - "repos/{owner}/{repo}/releases/{release_id}/assets/{attachment_id}" - )) - .await - } - - pub async fn edit_release_attachment( - &self, - owner: &str, - repo: &str, - release_id: u64, - attachment_id: u64, - opts: EditAttachmentOption, - ) -> Result<Attachment, ForgejoError> { - self.patch( - &format!("repos/{owner}/{repo}/releases/{release_id}/assets/{attachment_id}"), - &opts, - ) - .await - } - - pub async fn create_release( - &self, - owner: &str, - repo: &str, - opts: CreateReleaseOption, - ) -> Result<Release, ForgejoError> { - self.post(&format!("repos/{owner}/{repo}/releases"), &opts) - .await - } - - pub async fn latest_release( - &self, - owner: &str, - repo: &str, - ) -> Result<Option<Release>, ForgejoError> { - self.get_opt(&format!("repos/{owner}/{repo}/releases/latest")) - .await - } - - pub async fn download_zip_archive( - &self, - owner: &str, - repo: &str, - target: &str, - ) -> Result<Option<bytes::Bytes>, ForgejoError> { - let request = self - .client - .get( - self.url - .join(&format!("api/v1/repos/{owner}/{repo}/archive/{target}.zip")) - .unwrap(), - ) - .build()?; - self.execute_opt_raw(request).await - } - - pub async fn download_tarball_archive( - &self, - owner: &str, - repo: &str, - target: &str, - ) -> Result<Option<bytes::Bytes>, ForgejoError> { - let request = self - .client - .get( - self.url - .join(&format!( - "api/v1/repos/{owner}/{repo}/archive/{target}.tar.gz" - )) - .unwrap(), - ) - .build()?; - self.execute_opt_raw(request).await - } - - pub async fn download_release_attachment( - &self, - owner: &str, - repo: &str, - release: u64, - attach: u64, - ) -> Result<Option<bytes::Bytes>, ForgejoError> { - let release = self - .get_release_attachment(owner, repo, release, attach) - .await?; - let request = self.client.get(release.browser_download_url).build()?; - self.execute_opt_raw(request).await - } - - pub async fn get_tags( - &self, - owner: &str, - repo: &str, - query: TagQuery, - ) -> Result<Vec<Tag>, ForgejoError> { - self.get(&query.to_string(owner, repo)).await - } - - pub async fn create_tag( - &self, - owner: &str, - repo: &str, - opts: CreateTagOption, - ) -> Result<Tag, ForgejoError> { - self.post(&format!("repos/{owner}/{repo}/tags"), &opts) - .await - } - - pub async fn get_tag( - &self, - owner: &str, - repo: &str, - tag: &str, - ) -> Result<Option<Tag>, ForgejoError> { - self.get_opt(&format!("repos/{owner}/{repo}/tags/{tag}")) - .await - } - - pub async fn delete_tag(&self, owner: &str, repo: &str, tag: &str) -> Result<(), ForgejoError> { - self.delete(&format!("repos/{owner}/{repo}/tags/{tag}")) - .await - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Repository { - pub allow_merge_commits: bool, - pub allow_rebase: bool, - pub allow_rebase_explicit: bool, - pub allow_rebase_update: bool, - pub allow_squash_merge: bool, - pub archived: bool, - #[serde(with = "time::serde::rfc3339::option")] - pub archived_at: Option<time::OffsetDateTime>, - #[serde(deserialize_with = "crate::none_if_blank_url")] - pub avatar_url: Option<Url>, - pub clone_url: Url, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub default_allow_maintainer_edit: bool, - pub default_branch: String, - pub default_delete_branch_after_merge: bool, - pub default_merge_style: String, - pub description: String, - pub empty: bool, - pub external_tracker: Option<ExternalTracker>, - pub external_wiki: Option<ExternalWiki>, - pub fork: bool, - pub forks_count: u64, - pub full_name: String, - pub has_actions: bool, - pub has_issues: bool, - pub has_packages: bool, - pub has_projects: bool, - pub has_pull_requests: bool, - pub has_releases: bool, - pub has_wiki: bool, - pub html_url: Url, - pub id: u64, - pub ignore_whitespace_conflicts: bool, - pub internal: bool, - pub internal_tracker: Option<InternalTracker>, - pub language: String, - pub languages_url: Url, - pub link: String, - pub mirror: bool, - pub mirror_interval: Option<String>, - #[serde(with = "time::serde::rfc3339::option")] - pub mirror_updated: Option<time::OffsetDateTime>, - pub name: String, - pub open_issues_count: u64, - pub open_pr_counter: u64, - #[serde(deserialize_with = "crate::none_if_blank_url")] - pub original_url: Option<Url>, - pub owner: User, - pub parent: Option<Box<Repository>>, - pub permissions: Permission, - pub private: bool, - pub release_counter: u64, - pub repo_transfer: Option<RepoTransfer>, - pub size: u64, - pub ssh_url: String, - pub stars_count: u64, - pub template: bool, - #[serde(with = "time::serde::rfc3339")] - pub updated_at: time::OffsetDateTime, - pub url: Url, - pub watchers_count: u64, - pub website: Option<String>, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct RepositoryMeta { - pub full_name: String, - pub id: u64, - pub name: String, - pub owner: String, -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub struct CreateRepoOption { - pub auto_init: bool, - pub default_branch: String, - pub description: Option<String>, - pub gitignores: String, - pub issue_labels: String, - pub license: String, - pub name: String, - pub private: bool, - pub readme: String, - pub template: bool, - pub trust_model: TrustModel, -} - -#[derive(serde::Serialize, Debug, PartialEq)] -pub enum TrustModel { - Default, - Collaborator, - Committer, - #[serde(rename = "collaboratorcommiter")] - CollaboratorCommitter, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Milestone { - #[serde(with = "time::serde::rfc3339::option")] - pub closed_at: Option<time::OffsetDateTime>, - pub closed_issues: u64, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub description: String, - #[serde(with = "time::serde::rfc3339::option")] - pub due_on: Option<time::OffsetDateTime>, - pub id: u64, - pub open_issues: u64, - pub state: State, - pub title: String, - #[serde(with = "time::serde::rfc3339")] - pub updated_at: time::OffsetDateTime, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct PullRequest { - pub allow_maintainer_edit: bool, - pub assignee: User, - pub assignees: Vec<User>, - pub base: PrBranchInfo, - pub body: String, - #[serde(with = "time::serde::rfc3339::option")] - pub closed_at: Option<time::OffsetDateTime>, - pub comments: u64, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub diff_url: Url, - #[serde(with = "time::serde::rfc3339::option")] - pub due_date: Option<time::OffsetDateTime>, - pub head: PrBranchInfo, - pub html_url: Url, - pub id: u64, - pub is_locked: bool, - pub labels: Vec<Label>, - pub merge_base: String, - pub merge_commit_sha: Option<String>, - pub mergeable: bool, - pub merged: bool, - #[serde(with = "time::serde::rfc3339::option")] - pub merged_at: Option<time::OffsetDateTime>, - pub merged_by: Option<User>, - pub milestone: Option<Milestone>, - pub number: u64, - pub patch_url: Url, - pub pin_order: u64, - pub requested_reviewers: Option<Vec<User>>, - pub state: State, - pub title: String, - #[serde(with = "time::serde::rfc3339")] - pub updated_at: time::OffsetDateTime, - pub url: Url, - pub user: User, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct PrBranchInfo { - pub label: String, - #[serde(rename = "ref")] - pub _ref: String, - pub repo: Repository, - pub repo_id: u64, - pub sha: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct PullRequestMeta { - pub merged: bool, - #[serde(with = "time::serde::rfc3339::option")] - pub merged_at: Option<time::OffsetDateTime>, -} - -#[derive(Debug)] -pub struct PullQuery { - pub state: Option<State>, - pub sort: Option<PullQuerySort>, - pub milestone: Option<u64>, - pub labels: Vec<u64>, - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl PullQuery { - fn to_string(&self, owner: &str, repo: &str) -> String { - use std::fmt::Write; - // This is different to other query struct serialization because - // `labels` is serialized so strangely - let mut s = String::new(); - s.push_str("repos/"); - s.push_str(owner); - s.push('/'); - s.push_str(repo); - s.push_str("/pulls?"); - if let Some(state) = self.state { - s.push_str("state="); - s.push_str(state.as_str()); - s.push('&'); - } - if let Some(sort) = self.sort { - s.push_str("sort="); - s.push_str(sort.as_str()); - s.push('&'); - } - if let Some(milestone) = self.milestone { - s.push_str("sort="); - s.write_fmt(format_args!("{milestone}")) - .expect("writing to a string never fails"); - s.push('&'); - } - for label in &self.labels { - s.push_str("labels="); - s.write_fmt(format_args!("{label}")) - .expect("writing to a string never fails"); - s.push('&'); - } - if let Some(page) = self.page { - s.push_str("page="); - s.write_fmt(format_args!("{page}")) - .expect("writing to a string never fails"); - s.push('&'); - } - if let Some(limit) = self.limit { - s.push_str("limit="); - s.write_fmt(format_args!("{limit}")) - .expect("writing to a string never fails"); - s.push('&'); - } - s - } -} - -#[derive(Clone, Copy, Debug)] -pub enum PullQuerySort { - Oldest, - RecentUpdate, - LeastUpdate, - MostComment, - LeastComment, - Priority, -} - -impl PullQuerySort { - fn as_str(&self) -> &'static str { - match self { - PullQuerySort::Oldest => "oldest", - PullQuerySort::RecentUpdate => "recentupdate", - PullQuerySort::LeastUpdate => "leastupdate", - PullQuerySort::MostComment => "mostcomment", - PullQuerySort::LeastComment => "leastcomment", - PullQuerySort::Priority => "priority", - } - } -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct CreatePullRequestOption { - pub assignee: Option<String>, - pub assignees: Vec<String>, - pub base: String, - pub body: String, - #[serde(with = "time::serde::rfc3339::option")] - pub due_date: Option<time::OffsetDateTime>, - pub head: String, - pub labels: Vec<u64>, - pub milestone: Option<u64>, - pub title: String, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct MergePullRequestOption { - #[serde(rename = "Do")] - pub act: MergePrAction, - #[serde(rename = "MergeCommitId")] - pub merge_commit_id: Option<String>, - #[serde(rename = "MergeMessageField")] - pub merge_message_field: Option<String>, - #[serde(rename = "MergeTitleField")] - pub merge_title_field: Option<String>, - pub delete_branch_after_merge: Option<bool>, - pub force_merge: Option<bool>, - pub head_commit_id: Option<String>, - pub merge_when_checks_succeed: Option<bool>, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub enum MergePrAction { - #[serde(rename = "merge")] - #[default] - Merge, - #[serde(rename = "rebase")] - Rebase, - #[serde(rename = "rebase-merge")] - RebaseMerge, - #[serde(rename = "squash")] - Squash, - #[serde(rename = "manually-merged")] - ManuallyMerged, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Release { - pub assets: Vec<Attachment>, - pub author: User, - pub body: String, - #[serde(with = "time::serde::rfc3339")] - pub created_at: time::OffsetDateTime, - pub draft: bool, - pub html_url: Url, - pub id: u64, - pub name: String, - pub prerelease: bool, - #[serde(with = "time::serde::rfc3339")] - pub published_at: time::OffsetDateTime, - pub tag_name: String, - pub tarball_url: Url, - pub target_commitish: String, - pub url: Url, - pub zipball_url: Url, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct CreateReleaseOption { - pub body: String, - pub draft: bool, - pub name: String, - pub prerelease: bool, - pub tag_name: String, - pub target_commitish: Option<String>, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct EditReleaseOption { - pub body: Option<String>, - pub draft: Option<bool>, - pub name: Option<String>, - pub prerelease: Option<bool>, - pub tag_name: Option<String>, - pub target_commitish: Option<String>, -} - -#[derive(Default, Debug)] -pub struct ReleaseQuery { - pub draft: Option<bool>, - pub prerelease: Option<bool>, - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl ReleaseQuery { - fn to_string(&self, owner: &str, repo: &str) -> String { - format!( - "repos/{owner}/{repo}/releases?draft={}&pre-release={}&page={}&limit={}", - opt_bool_s(self.draft), - opt_bool_s(self.prerelease), - self.page.map(|page| page.to_string()).unwrap_or_default(), - self.limit.map(|page| page.to_string()).unwrap_or_default(), - ) - } -} - -fn opt_bool_s(b: Option<bool>) -> &'static str { - match b { - Some(true) => "true", - Some(false) => "false", - None => "", - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Tag { - pub commit: CommitMeta, - pub id: String, - pub message: String, - pub name: String, - pub tarball_url: Url, - pub zipball_url: Url, -} - -#[derive(serde::Serialize, Debug, PartialEq, Default)] -pub struct CreateTagOption { - pub message: Option<String>, - pub tag_name: String, - pub target: Option<String>, -} - -#[derive(Default, Debug)] -pub struct TagQuery { - pub page: Option<u32>, - pub limit: Option<u32>, -} - -impl TagQuery { - fn to_string(&self, owner: &str, repo: &str) -> String { - format!( - "repos/{owner}/{repo}/tags?page={}&limit={}", - self.page.map(|page| page.to_string()).unwrap_or_default(), - self.limit.map(|page| page.to_string()).unwrap_or_default(), - ) - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct CommitMeta { - #[serde(with = "time::serde::rfc3339")] - pub created: time::OffsetDateTime, - pub url: Url, - pub sha: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct ExternalTracker { - #[serde(rename = "external_tracker_format")] - pub format: String, - #[serde(rename = "external_tracker_regexp_pattern")] - pub regexp_pattern: String, - #[serde(rename = "external_tracker_style")] - pub style: String, - #[serde(rename = "external_tracker_url")] - pub url: Url, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct InternalTracker { - pub allow_only_contributors_to_track_time: bool, - pub enable_issue_dependencies: bool, - pub enable_time_tracker: bool, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct ExternalWiki { - #[serde(rename = "external_wiki_url")] - pub url: Url, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct Permission { - pub admin: bool, - pub pull: bool, - pub push: bool, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct RepoTransfer { - pub doer: User, - pub recipient: User, - pub teams: Vec<Team>, -} diff --git a/src/user.rs b/src/user.rs deleted file mode 100644 index a99a5e2..0000000 --- a/src/user.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::*; - -/// User operations. -impl Forgejo { - /// Returns info about the authorized user. - pub async fn myself(&self) -> Result<User, ForgejoError> { - self.get("user").await - } - - /// Returns info about the specified user. - pub async fn get_user(&self, user: &str) -> Result<Option<User>, ForgejoError> { - self.get_opt(&format!("users/{user}/")).await - } - - /// Gets the list of users that follow the specified user. - pub async fn get_followers(&self, user: &str) -> Result<Option<Vec<User>>, ForgejoError> { - self.get_opt(&format!("users/{user}/followers/")).await - } - - /// Gets the list of users the specified user is following. - pub async fn get_following(&self, user: &str) -> Result<Option<Vec<User>>, ForgejoError> { - self.get_opt(&format!("users/{user}/following/")).await - } -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub struct User { - pub active: bool, - pub avatar_url: Url, - #[serde(with = "time::serde::rfc3339")] - pub created: time::OffsetDateTime, - pub description: String, - pub email: String, - pub followers_count: u64, - pub following_count: u64, - pub full_name: String, - pub id: u64, - pub is_admin: bool, - pub language: String, - #[serde(with = "time::serde::rfc3339")] - pub last_login: time::OffsetDateTime, - pub location: String, - pub login: String, - pub login_name: String, - pub prohibit_login: bool, - pub restricted: bool, - pub starred_repos_count: u64, - pub website: String, -} - -#[derive(serde::Deserialize, Debug, PartialEq)] -pub enum UserVisibility { - #[serde(rename = "public")] - Public, - #[serde(rename = "limited")] - Limited, - #[serde(rename = "private")] - Private, -} |