// 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 . 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, } #[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>, 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, pub updated_at: DateTime, pub archived_at: DateTime, pub permissions: Option, pub has_issues: bool, pub internal_tracker: Option, pub external_tracker: Option, pub has_wiki: bool, pub external_wiki: Option, pub wiki_branch: Option, 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>, pub repo_transfer: Option, pub topics: Option>, } #[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>, pub created: Option>, 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, 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, pub body: Option, #[serde(rename(deserialize = "ref"))] pub ref_: Option, } #[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>, 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, pub labels: Option>, pub milestone: Option, pub assignee: Option, pub assignees: Option>, pub state: IssueState, pub is_locked: bool, pub comments: u32, pub created_at: DateTime, pub updated_at: Option>, pub closed_at: Option>, pub pull_request: Option, 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, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Deserialize, Debug, PartialEq)] pub struct Issue { pub action: IssueAction, pub changes: Option, pub issue: IssueAttrs, pub comment: Option, pub repository: Repository, pub is_pull: Option, } impl From for Hook { fn from(push: Push) -> Self { Self::Push(push) } } impl From for Hook { fn from(branch: AddedBranch) -> Self { Self::AddedBranch(branch) } } impl From for Hook { fn from(branch: RemovedBranch) -> Self { Self::RemovedBranch(branch) } } impl From 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(payload: &[u8]) -> Result 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 { Ok(match event { "create" => parse_payload::(payload).map(Hook::from), "delete" => parse_payload::(payload).map(Hook::from), "push" => parse_payload::(payload).map(Hook::from), "issues" => parse_payload::(payload).map(Hook::from), "issue_comment" => parse_payload::(payload).map(Hook::from), _ => return Err(Error::UnknownHookType), }?) } }