1
0
Fork 0

replace with generated api

This commit is contained in:
Cyborus 2024-01-18 13:44:07 -05:00
parent 77b390575a
commit 81b17abc8a
No known key found for this signature in database
10 changed files with 12272 additions and 2491 deletions

View file

@ -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,
}

12253
src/generated.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -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,
}

View file

@ -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;
mod generated;
pub use admin::*;
pub use issue::*;
pub use misc::*;
pub use notification::*;
pub use organization::*;
pub use package::*;
pub use repository::*;
pub use user::*;
pub use generated::structs;
#[derive(thiserror::Error, Debug)]
pub enum ForgejoError {
@ -134,202 +119,35 @@ 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
fn get(&self, path: &str) -> reqwest::RequestBuilder {
let url = self.url.join("api/v1").unwrap().join(path).unwrap();
self.client.get(url)
}
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
fn put(&self, path: &str) -> reqwest::RequestBuilder {
let url = self.url.join("api/v1").unwrap().join(path).unwrap();
self.client.put(url)
}
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
fn post(&self, path: &str) -> reqwest::RequestBuilder {
let url = self.url.join("api/v1").unwrap().join(path).unwrap();
self.client.post(url)
}
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
fn delete(&self, path: &str) -> reqwest::RequestBuilder {
let url = self.url.join("api/v1").unwrap().join(path).unwrap();
self.client.post(url)
}
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
fn patch(&self, path: &str) -> reqwest::RequestBuilder {
let url = self.url.join("api/v1").unwrap().join(path).unwrap();
self.client.post(url)
}
async fn post_multipart<T: DeserializeOwned>(
&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
}
async fn post_unit<T: Serialize>(&self, path: &str, body: &T) -> Result<(), ForgejoError> {
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
}
async fn post_raw(&self, path: &str, body: String) -> Result<String, ForgejoError> {
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
}
async fn delete(&self, path: &str) -> Result<(), ForgejoError> {
let url = self.url.join("api/v1/").unwrap().join(path).unwrap();
let request = self.client.delete(url).build()?;
self.execute_unit(request).await
}
async fn patch<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.patch(url).json(body).build()?;
self.execute(request).await
}
async fn put<T: DeserializeOwned>(&self, path: &str) -> Result<T, ForgejoError> {
let url = self.url.join("api/v1/").unwrap().join(path).unwrap();
let request = self.client.put(url).build()?;
self.execute(request).await
}
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_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_success() => Ok(response),
status if status.is_client_error() => Err(ForgejoError::ApiError(
status,
response

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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>,
}

View file

@ -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,
}

View file

@ -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>,
}

View file

@ -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,
}