350 lines
8.4 KiB
Rust
350 lines
8.4 KiB
Rust
// Copyright (C) 2024-2099 The crate authors.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it
|
|
// under the terms of the GNU Affero General Public License as published by the
|
|
// Free Software Foundation, either version 3 of the License, or (at your
|
|
// option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
|
// for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
mod error;
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
pub use error::Error;
|
|
|
|
use chrono::{DateTime, Utc};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct CommitAuthor {
|
|
pub name: String,
|
|
pub email: String,
|
|
pub username: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct Commit {
|
|
pub id: String,
|
|
pub message: String,
|
|
pub url: String,
|
|
pub author: CommitAuthor,
|
|
pub committer: CommitAuthor,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct Permissions {
|
|
admin: bool,
|
|
push: bool,
|
|
pull: bool,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct InternalTracker {
|
|
enable_time_tracker: bool,
|
|
allow_only_contributors_to_track_time: bool,
|
|
enable_issue_dependencies: bool,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct ExternalTracker {
|
|
pub external_tracker_url: String,
|
|
pub external_tracker_format: String,
|
|
pub external_tracker_style: String,
|
|
pub external_tracker_regexp_pattern: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct ExternalWiki {
|
|
pub external_wiki_url: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct RepositoryMeta {
|
|
pub id: u64,
|
|
pub name: String,
|
|
pub owner: String,
|
|
pub full_name: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct Repository {
|
|
pub id: u64,
|
|
pub owner: User,
|
|
pub name: String,
|
|
pub full_name: String,
|
|
pub description: String,
|
|
pub empty: bool,
|
|
pub private: bool,
|
|
pub fork: bool,
|
|
pub template: bool,
|
|
pub parent: Option<Box<Repository>>,
|
|
pub mirror: bool,
|
|
pub size: u32,
|
|
pub language: String,
|
|
pub languages_url: String,
|
|
pub html_url: String,
|
|
pub url: String,
|
|
pub link: String,
|
|
pub ssh_url: String,
|
|
pub clone_url: String,
|
|
pub website: String,
|
|
pub stars_count: u32,
|
|
pub forks_count: u32,
|
|
pub watchers_count: u32,
|
|
pub open_issues_count: u32,
|
|
pub open_pr_counter: u32,
|
|
pub release_counter: u32,
|
|
pub default_branch: String,
|
|
pub archived: bool,
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
pub archived_at: DateTime<Utc>,
|
|
pub permissions: Option<Permissions>,
|
|
pub has_issues: bool,
|
|
pub internal_tracker: Option<InternalTracker>,
|
|
pub external_tracker: Option<ExternalTracker>,
|
|
pub has_wiki: bool,
|
|
pub external_wiki: Option<ExternalWiki>,
|
|
pub wiki_branch: Option<String>,
|
|
pub globally_editable_wiki: bool,
|
|
pub has_pull_requests: bool,
|
|
pub has_projects: bool,
|
|
pub has_releases: bool,
|
|
pub has_actions: bool,
|
|
pub ignore_whitespace_conflicts: bool,
|
|
pub allow_merge_commits: bool,
|
|
pub allow_rebase: bool,
|
|
pub allow_rebase_explicit: bool,
|
|
pub allow_squash_merge: bool,
|
|
pub allow_rebase_update: bool,
|
|
pub default_delete_branch_after_merge: bool,
|
|
pub default_merge_style: String,
|
|
pub default_allow_maintainer_edit: bool,
|
|
pub avatar_url: String,
|
|
pub internal: bool,
|
|
pub mirror_interval: String,
|
|
pub object_format_name: String,
|
|
pub mirror_updated: Option<DateTime<Utc>>,
|
|
pub repo_transfer: Option<String>,
|
|
pub topics: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct User {
|
|
pub id: u64,
|
|
pub login: String,
|
|
pub login_name: String,
|
|
pub source_id: u64,
|
|
pub full_name: String,
|
|
pub email: String,
|
|
pub avatar_url: String,
|
|
pub html_url: String,
|
|
pub language: String,
|
|
pub is_admin: bool,
|
|
pub last_login: Option<DateTime<Utc>>,
|
|
pub created: Option<DateTime<Utc>>,
|
|
pub restricted: bool,
|
|
pub active: bool,
|
|
pub prohibit_login: bool,
|
|
pub location: String,
|
|
pub pronouns: String,
|
|
pub website: String,
|
|
pub description: String,
|
|
pub visibility: String,
|
|
pub followers_count: u32,
|
|
pub following_count: u32,
|
|
pub starred_repos_count: u32,
|
|
pub username: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct Push {
|
|
#[serde(rename(deserialize = "ref"))]
|
|
pub ref_: String,
|
|
pub before: String,
|
|
pub after: String,
|
|
pub compare_url: String,
|
|
pub commits: Vec<Commit>,
|
|
pub total_commits: u32,
|
|
pub repository: Repository,
|
|
pub pusher: User,
|
|
pub sender: User,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub enum RefType {
|
|
Branch,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct AddedBranch {
|
|
pub sha: String,
|
|
pub ref_type: String,
|
|
pub repository: Repository,
|
|
pub sender: User,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct RemovedBranch {
|
|
pub sha: String,
|
|
pub ref_type: String,
|
|
pub pusher_type: String,
|
|
pub repository: Repository,
|
|
pub sender: User,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum IssueAction {
|
|
Created,
|
|
Opened,
|
|
Edited,
|
|
Closed,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct ChangesFromPayload {
|
|
pub from: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct Changes {
|
|
pub title: Option<ChangesFromPayload>,
|
|
pub body: Option<ChangesFromPayload>,
|
|
#[serde(rename(deserialize = "ref"))]
|
|
pub ref_: Option<ChangesFromPayload>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum IssueState {
|
|
Open,
|
|
Closed,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct PullRequestMeta {
|
|
pub merged: bool,
|
|
pub merged_at: Option<DateTime<Utc>>,
|
|
pub draft: bool,
|
|
pub html_url: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct IssueAttrs {
|
|
pub id: u32,
|
|
pub url: String,
|
|
pub html_url: String,
|
|
pub number: u32,
|
|
pub user: User,
|
|
pub original_author: String,
|
|
pub original_author_id: u32,
|
|
pub title: String,
|
|
pub body: String,
|
|
#[serde(rename(deserialize = "ref"))]
|
|
pub ref_: String,
|
|
pub assets: Vec<String>,
|
|
pub labels: Option<Vec<String>>,
|
|
pub milestone: Option<String>,
|
|
pub assignee: Option<User>,
|
|
pub assignees: Option<Vec<User>>,
|
|
pub state: IssueState,
|
|
pub is_locked: bool,
|
|
pub comments: u32,
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: Option<DateTime<Utc>>,
|
|
pub closed_at: Option<DateTime<Utc>>,
|
|
pub pull_request: Option<PullRequestMeta>,
|
|
pub repository: RepositoryMeta,
|
|
pub pin_order: u32,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct Comment {
|
|
pub id: u64,
|
|
pub html_url: String,
|
|
pub pull_request_url: String,
|
|
pub issue_url: String,
|
|
pub user: User,
|
|
pub original_author: String,
|
|
pub original_author_id: u64,
|
|
pub body: String,
|
|
pub assets: Vec<String>,
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
pub struct Issue {
|
|
pub action: IssueAction,
|
|
pub changes: Option<Changes>,
|
|
pub issue: IssueAttrs,
|
|
pub comment: Option<Comment>,
|
|
pub repository: Repository,
|
|
pub is_pull: Option<bool>,
|
|
}
|
|
|
|
impl From<Push> for Hook {
|
|
fn from(push: Push) -> Self {
|
|
Self::Push(push)
|
|
}
|
|
}
|
|
|
|
impl From<AddedBranch> for Hook {
|
|
fn from(branch: AddedBranch) -> Self {
|
|
Self::AddedBranch(branch)
|
|
}
|
|
}
|
|
|
|
impl From<RemovedBranch> for Hook {
|
|
fn from(branch: RemovedBranch) -> Self {
|
|
Self::RemovedBranch(branch)
|
|
}
|
|
}
|
|
|
|
impl From<Issue> for Hook {
|
|
fn from(issue: Issue) -> Self {
|
|
Self::Issue(issue)
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, PartialEq)]
|
|
#[serde(untagged)]
|
|
pub enum Hook {
|
|
Push(Push),
|
|
AddedBranch(AddedBranch),
|
|
RemovedBranch(RemovedBranch),
|
|
Issue(Issue),
|
|
}
|
|
|
|
/// Deserializes the payload into a struct
|
|
fn parse_payload<Output>(payload: &[u8]) -> Result<Output, serde_json::Error>
|
|
where
|
|
for<'a> Output: Deserialize<'a>,
|
|
{
|
|
serde_json::from_slice(payload)
|
|
}
|
|
|
|
impl Hook {
|
|
/// Generate the proper struct based on the event type provided by Forgejo. Generally found in
|
|
/// the "X-Forgejo-Event" http header, which is not the same as the "X-Forgejo-Event-Type"
|
|
/// header.
|
|
pub fn try_from_event(event: &str, payload: &[u8]) -> Result<Hook, Error> {
|
|
Ok(match event {
|
|
"create" => parse_payload::<AddedBranch>(payload).map(Hook::from),
|
|
"delete" => parse_payload::<RemovedBranch>(payload).map(Hook::from),
|
|
"push" => parse_payload::<Push>(payload).map(Hook::from),
|
|
"issues" => parse_payload::<Issue>(payload).map(Hook::from),
|
|
"issue_comment" => parse_payload::<Issue>(payload).map(Hook::from),
|
|
_ => return Err(Error::UnknownHookType),
|
|
}?)
|
|
}
|
|
}
|