diff --git a/src/error.rs b/src/error.rs index 14ac4b0..c00d02c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,7 +23,7 @@ use hmac::digest::InvalidLength as HmacInvalidLength; use hyper::StatusCode; #[derive(Debug)] -pub(crate) enum Error { +pub enum Error { MethodMismatch(hyper::Method), InvalidSecret, InvalidContentType, diff --git a/src/hooks/forgejo.rs b/src/hooks/forgejo.rs new file mode 100644 index 0000000..fcfe428 --- /dev/null +++ b/src/hooks/forgejo.rs @@ -0,0 +1,48 @@ +// 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 . + +use crate::hooks::types::{Commit, Hook, Push, Repository, User}; +use crate::Error; + +pub use ::forgejo_hooks::Hook as ForgejoHook; + +impl TryFrom for Hook { + type Error = Error; + + fn try_from(hook: ForgejoHook) -> Result { + Ok(match hook { + ForgejoHook::Push(push) => Hook::Push(Push { + ref_: push.ref_, + object_kind: String::from("push"), + commits: push + .commits + .into_iter() + .map(|commit| Commit { + ref_: commit.id, + message: commit.message, + url: commit.url, + }) + .collect(), + repository: Repository { + name: push.repository.name, + }, + pusher: User { + name: push.pusher.login, + }, + }), + _ => return Err(Error::UnsupportedHookConversion), + }) + } +} diff --git a/src/hooks/gitlab.rs b/src/hooks/gitlab.rs new file mode 100644 index 0000000..ef60cd6 --- /dev/null +++ b/src/hooks/gitlab.rs @@ -0,0 +1,164 @@ +// 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 . + +use crate::hooks::types::{ + Commit, Hook, Issue, IssueAction, IssueAttrs, MergeRequest, MergeRequestAction, + MergeRequestAttrs, Note, Push, Repository, User, +}; +use crate::Error; + +pub use ::gitlab::webhooks::{ + CommitHookAttrs, IssueAction as GlIssueAction, IssueHook as GlIssueHook, + IssueHookAttrs as GlIssueHookAttrs, MergeRequestAction as GlMergeRequestAction, + MergeRequestHook as GlMergeRequestHook, MergeRequestHookAttrs as GlMergeRequestHookAttrs, + NoteHook as GlNoteHook, PushHook as GlPushHook, WebHook as GitlabHook, +}; + +impl From for Commit { + fn from(other: CommitHookAttrs) -> Commit { + Commit { + ref_: other.id, + message: other.message, + url: other.url, + } + } +} + +impl From for Push { + fn from(other: GlPushHook) -> Push { + Push { + ref_: other.ref_, + object_kind: other.object_kind, + commits: other.commits.into_iter().map(Into::into).collect(), + repository: Repository { + name: other.project.name, + }, + pusher: User { + name: other.user_name, + }, + } + } +} + +impl From for IssueAction { + fn from(other: GlIssueAction) -> IssueAction { + match other { + GlIssueAction::Update => IssueAction::Update, + GlIssueAction::Open => IssueAction::Open, + GlIssueAction::Close => IssueAction::Close, + GlIssueAction::Reopen => IssueAction::Reopen, + } + } +} + +impl From for Issue { + fn from(other: GlIssueHook) -> Issue { + Issue { + action: other.object_attributes.action.map(Into::into), + repository: Repository { + name: other.project.name, + }, + author: User { + name: other.user.name, + }, + id: other.object_attributes.iid, + title: other.object_attributes.title, + url: other.object_attributes.url, + } + } +} + +impl From for IssueAttrs { + fn from(other: GlIssueHookAttrs) -> IssueAttrs { + IssueAttrs { + id: other.iid, + title: other.title, + } + } +} + +impl From for MergeRequestAction { + fn from(other: GlMergeRequestAction) -> MergeRequestAction { + match other { + GlMergeRequestAction::Update => MergeRequestAction::Update, + GlMergeRequestAction::Open => MergeRequestAction::Open, + GlMergeRequestAction::Close => MergeRequestAction::Close, + GlMergeRequestAction::Reopen => MergeRequestAction::Reopen, + GlMergeRequestAction::Merge => MergeRequestAction::Merge, + GlMergeRequestAction::Approved => MergeRequestAction::Approved, + GlMergeRequestAction::Unapproved => MergeRequestAction::Unapproved, + GlMergeRequestAction::Approval => MergeRequestAction::Approval, + GlMergeRequestAction::Unapproval => MergeRequestAction::Unapproval, + } + } +} + +impl From for MergeRequest { + fn from(other: GlMergeRequestHook) -> MergeRequest { + MergeRequest { + action: other.object_attributes.action.map(Into::into), + repository: Repository { + name: other.project.name, + }, + author: User { + name: other.user.name, + }, + id: other.object_attributes.iid, + title: other.object_attributes.title, + url: other.object_attributes.url, + } + } +} + +impl From for MergeRequestAttrs { + fn from(other: GlMergeRequestHookAttrs) -> MergeRequestAttrs { + MergeRequestAttrs { + id: other.id, + title: other.title, + } + } +} + +impl From for Note { + fn from(other: GlNoteHook) -> Note { + Note { + snippet: other.snippet.is_some(), + commit: other.commit.map(Into::into), + issue: other.issue.map(Into::into), + merge_request: other.merge_request.map(Into::into), + repository: Repository { + name: other.project.name, + }, + author: User { + name: other.user.name, + }, + url: other.object_attributes.url, + } + } +} + +impl TryFrom for Hook { + type Error = Error; + + fn try_from(hook: GitlabHook) -> Result { + Ok(match hook { + GitlabHook::Push(push) => Hook::Push((*push).into()), + GitlabHook::Issue(issue) => Hook::Issue((*issue).into()), + GitlabHook::MergeRequest(mr) => Hook::MergeRequest((*mr).into()), + GitlabHook::Note(note) => Hook::Note((*note).into()), + _ => return Err(Error::UnsupportedHookConversion), + }) + } +} diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs index c45461e..e92748d 100644 --- a/src/hooks/mod.rs +++ b/src/hooks/mod.rs @@ -13,296 +13,16 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use crate::Error; +mod forgejo; +mod gitlab; +mod types; -pub use forgejo_hooks::Hook as ForgejoHook; -pub use gitlab::webhooks::{ - CommitHookAttrs, IssueAction as GlIssueAction, IssueHook as GlIssueHook, - IssueHookAttrs as GlIssueHookAttrs, MergeRequestAction as GlMergeRequestAction, - MergeRequestHook as GlMergeRequestHook, MergeRequestHookAttrs as GlMergeRequestHookAttrs, - NoteHook as GlNoteHook, PushHook as GlPushHook, WebHook as GitlabHook, WikiPageAction, -}; -use log::debug; +pub use crate::hooks::forgejo::ForgejoHook; +pub use crate::hooks::gitlab::GitlabHook; +pub use crate::hooks::types::Hook; +use crate::hooks::types::{IssueAction, MergeRequestAction}; -/// Defines a generic user that can be used for many purposes. -#[derive(Debug, Clone)] -pub(crate) struct User { - /// Name of the user - name: String, -} - -#[derive(Debug, Clone)] -pub(crate) struct Commit { - /// Reference of the commit - ref_: String, - /// Commit message - message: String, - /// URL where the commit can be read at - url: String, -} - -#[derive(Debug, Clone)] -pub(crate) struct Repository { - /// Name of the project. - name: String, -} - -#[derive(Debug, Clone)] -pub(crate) struct Push { - /// Reference where commits have been pushed to. - ref_: String, - /// The event which occured. - object_kind: String, - /// Commit list. - commits: Vec, - /// Project repository. - repository: Repository, - /// Person who pushed the commits. It isn't necessarily the same as commit authors. - pusher: User, -} - -#[derive(Debug, Clone)] -pub(crate) enum IssueAction { - Update, - Open, - Close, - Reopen, -} - -#[derive(Debug, Clone)] -pub(crate) struct Issue { - action: Option, - repository: Repository, - author: User, - id: u64, - title: String, - url: Option, -} - -#[derive(Debug, Clone)] -pub(crate) struct IssueAttrs { - id: u64, - title: String, -} - -#[derive(Debug, Clone)] -pub(crate) enum MergeRequestAction { - Update, - Open, - Close, - Reopen, - Merge, - Approved, - Unapproved, - Approval, - Unapproval, -} - -#[derive(Debug, Clone)] -pub(crate) struct MergeRequest { - action: Option, - repository: Repository, - author: User, - id: u64, - title: String, - url: Option, -} - -#[derive(Debug, Clone)] -pub(crate) struct MergeRequestAttrs { - id: u64, - title: String, -} - -#[derive(Debug, Clone)] -pub(crate) struct Note { - snippet: bool, - commit: Option, - issue: Option, - merge_request: Option, - repository: Repository, - author: User, - url: String, -} - -/// Lowest common denominator struct so that we don't have to duplicate our code for each platform -/// we support. -#[derive(Debug)] -pub(crate) enum Hook { - /// Push event - Push(Push), - Issue(Issue), - MergeRequest(MergeRequest), - Note(Note), -} - -impl From for Commit { - fn from(other: CommitHookAttrs) -> Commit { - Commit { - ref_: other.id, - message: other.message, - url: other.url, - } - } -} - -impl From for Push { - fn from(other: GlPushHook) -> Push { - Push { - ref_: other.ref_, - object_kind: other.object_kind, - commits: other.commits.into_iter().map(Into::into).collect(), - repository: Repository { - name: other.project.name, - }, - pusher: User { - name: other.user_name, - }, - } - } -} - -impl From for IssueAction { - fn from(other: GlIssueAction) -> IssueAction { - match other { - GlIssueAction::Update => IssueAction::Update, - GlIssueAction::Open => IssueAction::Open, - GlIssueAction::Close => IssueAction::Close, - GlIssueAction::Reopen => IssueAction::Reopen, - } - } -} - -impl From for Issue { - fn from(other: GlIssueHook) -> Issue { - Issue { - action: other.object_attributes.action.map(Into::into), - repository: Repository { - name: other.project.name, - }, - author: User { - name: other.user.name, - }, - id: other.object_attributes.iid, - title: other.object_attributes.title, - url: other.object_attributes.url, - } - } -} - -impl From for IssueAttrs { - fn from(other: GlIssueHookAttrs) -> IssueAttrs { - IssueAttrs { - id: other.iid, - title: other.title, - } - } -} - -impl From for MergeRequestAction { - fn from(other: GlMergeRequestAction) -> MergeRequestAction { - match other { - GlMergeRequestAction::Update => MergeRequestAction::Update, - GlMergeRequestAction::Open => MergeRequestAction::Open, - GlMergeRequestAction::Close => MergeRequestAction::Close, - GlMergeRequestAction::Reopen => MergeRequestAction::Reopen, - GlMergeRequestAction::Merge => MergeRequestAction::Merge, - GlMergeRequestAction::Approved => MergeRequestAction::Approved, - GlMergeRequestAction::Unapproved => MergeRequestAction::Unapproved, - GlMergeRequestAction::Approval => MergeRequestAction::Approval, - GlMergeRequestAction::Unapproval => MergeRequestAction::Unapproval, - } - } -} - -impl From for MergeRequest { - fn from(other: GlMergeRequestHook) -> MergeRequest { - MergeRequest { - action: other.object_attributes.action.map(Into::into), - repository: Repository { - name: other.project.name, - }, - author: User { - name: other.user.name, - }, - id: other.object_attributes.iid, - title: other.object_attributes.title, - url: other.object_attributes.url, - } - } -} - -impl From for MergeRequestAttrs { - fn from(other: GlMergeRequestHookAttrs) -> MergeRequestAttrs { - MergeRequestAttrs { - id: other.id, - title: other.title, - } - } -} - -impl From for Note { - fn from(other: GlNoteHook) -> Note { - Note { - snippet: other.snippet.is_some(), - commit: other.commit.map(Into::into), - issue: other.issue.map(Into::into), - merge_request: other.merge_request.map(Into::into), - repository: Repository { - name: other.project.name, - }, - author: User { - name: other.user.name, - }, - url: other.object_attributes.url, - } - } -} - -impl TryFrom for Hook { - type Error = Error; - - fn try_from(hook: GitlabHook) -> Result { - Ok(match hook { - GitlabHook::Push(push) => Hook::Push((*push).into()), - GitlabHook::Issue(issue) => Hook::Issue((*issue).into()), - GitlabHook::MergeRequest(mr) => Hook::MergeRequest((*mr).into()), - GitlabHook::Note(note) => Hook::Note((*note).into()), - _ => return Err(Error::UnsupportedHookConversion), - }) - } -} - -impl TryFrom for Hook { - type Error = Error; - - fn try_from(hook: ForgejoHook) -> Result { - Ok(match hook { - ForgejoHook::Push(push) => Hook::Push(Push { - ref_: push.ref_, - object_kind: String::from("push"), - commits: push - .commits - .into_iter() - .map(|commit| Commit { - ref_: commit.id, - message: commit.message, - url: commit.url, - }) - .collect(), - repository: Repository { - name: push.repository.name, - }, - pusher: User { - name: push.pusher.login, - }, - }), - _ => return Err(Error::UnsupportedHookConversion), - }) - } -} - -pub(crate) fn format_hook(hook: &Hook) -> Option { +pub fn format_hook(hook: &Hook) -> Option { Some(match hook { Hook::Push(push) if push.object_kind == "tag_push" => { let ref_ = push.ref_.strip_prefix("refs/tags/").unwrap_or("?!"); diff --git a/src/hooks/types.rs b/src/hooks/types.rs new file mode 100644 index 0000000..f8d7169 --- /dev/null +++ b/src/hooks/types.rs @@ -0,0 +1,126 @@ +// 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 . + +/// Defines a generic user that can be used for many purposes. +#[derive(Debug, Clone)] +pub struct User { + /// Name of the user + pub name: String, +} + +#[derive(Debug, Clone)] +pub struct Commit { + /// Reference of the commit + pub ref_: String, + /// Commit message + pub message: String, + /// URL where the commit can be read at + pub url: String, +} + +#[derive(Debug, Clone)] +pub struct Repository { + /// Name of the project. + pub name: String, +} + +#[derive(Debug, Clone)] +pub struct Push { + /// Reference where commits have been pushed to. + pub ref_: String, + /// The event which occured. + pub object_kind: String, + /// Commit list. + pub commits: Vec, + /// Project repository. + pub repository: Repository, + /// Person who pushed the commits. It isn't necessarily the same as commit authors. + pub pusher: User, +} + +#[derive(Debug, Clone)] +pub enum IssueAction { + Update, + Open, + Close, + Reopen, +} + +#[derive(Debug, Clone)] +pub struct Issue { + pub action: Option, + pub repository: Repository, + pub author: User, + pub id: u64, + pub title: String, + pub url: Option, +} + +#[derive(Debug, Clone)] +pub struct IssueAttrs { + pub id: u64, + pub title: String, +} + +#[derive(Debug, Clone)] +pub enum MergeRequestAction { + Update, + Open, + Close, + Reopen, + Merge, + Approved, + Unapproved, + Approval, + Unapproval, +} + +#[derive(Debug, Clone)] +pub struct MergeRequest { + pub action: Option, + pub repository: Repository, + pub author: User, + pub id: u64, + pub title: String, + pub url: Option, +} + +#[derive(Debug, Clone)] +pub struct MergeRequestAttrs { + pub id: u64, + pub title: String, +} + +#[derive(Debug, Clone)] +pub struct Note { + pub snippet: bool, + pub commit: Option, + pub issue: Option, + pub merge_request: Option, + pub repository: Repository, + pub author: User, + pub url: String, +} + +/// Lowest common denominator struct so that we don't have to duplicate our code for each platform +/// we support. +#[derive(Debug)] +pub enum Hook { + /// Push event + Push(Push), + Issue(Issue), + MergeRequest(MergeRequest), + Note(Note), +}