diff --git a/src/bot.rs b/src/bot.rs index 25dea32..928da9b 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use crate::hook::{format_hook, GitlabHook}; +use crate::hook::{format_hook, Hook}; use log::debug; use xmpp::jid::{BareJid, Jid}; @@ -75,7 +75,7 @@ impl XmppClient { } } - pub async fn hook(&mut self, wh: GitlabHook) { + pub async fn hook(&mut self, wh: Hook) { debug!("Received Hook"); if let Some(display) = format_hook(&wh) { debug!("Hook: {}", display); diff --git a/src/error.rs b/src/error.rs index e45c86a..14ac4b0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,6 +29,7 @@ pub(crate) enum Error { InvalidContentType, InvalidSignature, InvalidRequest, + UnsupportedHookConversion, Hex(FromHexError), Hmac(HmacInvalidLength), Hyper(hyper::Error), @@ -64,6 +65,7 @@ impl std::fmt::Display for Error { Error::InvalidContentType => write!(fmt, "the content-type is invalid"), Error::InvalidSignature => write!(fmt, "the signature is invalid"), Error::InvalidRequest => write!(fmt, "the request is invalid"), + Error::UnsupportedHookConversion => write!(fmt, "Unable to convert hook"), Error::Hex(e) => write!(fmt, "hex error: {}", e), Error::Hmac(e) => write!(fmt, "hmac error: {}", e), Error::Hyper(e) => write!(fmt, "hyper error: {}", e), diff --git a/src/hook.rs b/src/hook.rs index 7a649f8..32fc1df 100644 --- a/src/hook.rs +++ b/src/hook.rs @@ -13,35 +13,123 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use crate::Error; + pub use forgejo_hooks::Hook as ForgejoHook; pub use gitlab::webhooks::{ IssueAction, MergeRequestAction, WebHook as GitlabHook, WikiPageAction, }; use log::debug; -#[derive(Debug)] -pub enum Hook { - Forgejo(ForgejoHook), - Gitlab(GitlabHook), +/// Defines a generic user that can be used for many purposes. +#[derive(Debug, Clone)] +pub(crate) struct User { + /// Name of the user + name: String, } -impl From for Hook { - fn from(hook: GitlabHook) -> Hook { - Hook::Gitlab(hook) +#[derive(Debug, Clone)] +pub(crate) struct Commit { + /// 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, +} + +/// 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), +} + +impl TryFrom for Hook { + type Error = Error; + + fn try_from(hook: GitlabHook) -> Result { + Ok(match hook { + GitlabHook::Push(push) => Hook::Push(Push { + ref_: push.ref_, + object_kind: push.object_kind, + commits: push + .commits + .into_iter() + .map(|commit| Commit { + message: commit.message, + url: commit.url, + }) + .collect(), + repository: Repository { + name: push.project.name, + }, + pusher: User { + name: push.user_name, + }, + }), + _ => return Err(Error::UnsupportedHookConversion), + }) } } -pub fn format_hook(glh: &GitlabHook) -> Option { - Some(match glh { - GitlabHook::Push(push) if push.object_kind == "tag_push" => { +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 { + 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 { + Some(match hook { + Hook::Push(push) if push.object_kind == "tag_push" => { + let ref_ = push.ref_.strip_prefix("refs/tags/").unwrap_or("?!"); format!( "[{}] {} pushed tag {}.", - push.project.name, - push.user_name, - push.ref_.strip_prefix("refs/tags/").unwrap_or("?!"), + push.repository.name, push.pusher.name, ref_ ) } - GitlabHook::Push(push) => { + Hook::Push(push) => { if push.ref_ != "refs/heads/main" { // Ignore: Action not on 'main' branch return None; @@ -53,8 +141,8 @@ pub fn format_hook(glh: &GitlabHook) -> Option { } let mut text = format!( "[{}] {} pushed {} commits to main", - push.project.name, - push.user_name, + push.repository.name, + push.pusher.name, push.commits.len(), ); // Display max 3 commits @@ -67,109 +155,110 @@ pub fn format_hook(glh: &GitlabHook) -> Option { } } text - } - GitlabHook::Issue(issue) => { - let action = match issue.object_attributes.action { - Some(IssueAction::Update) => return None, - Some(IssueAction::Open) => "opened", - Some(IssueAction::Close) => "closed", - Some(IssueAction::Reopen) => "reopened", - None => return None, - }; - format!( - "[{}] {} {} issue {}: {}{}", - issue.project.name, - issue.user.name, - action, - issue.object_attributes.iid, - issue.object_attributes.title, - issue - .object_attributes - .url - .as_ref() - .map(|url| format!(" <{}>", url)) - .unwrap_or("".to_owned()) - ) - } - GitlabHook::MergeRequest(merge_req) => { - let action = match merge_req.object_attributes.action { - Some(MergeRequestAction::Update) => return None, - Some(MergeRequestAction::Open) => "opened", - Some(MergeRequestAction::Close) => "closed", - Some(MergeRequestAction::Reopen) => "reopened", - Some(MergeRequestAction::Merge) => "merged", - None => return None, - _ => { - log::warn!( - "Unsupported merge request action: {:?}", - merge_req.object_attributes.action - ); - return None; - } - }; - format!( - "[{}] {} {} merge request {}: {}{}", - merge_req.project.name, - merge_req.user.name, - action, - merge_req.object_attributes.iid, - merge_req.object_attributes.title, - merge_req - .object_attributes - .url - .as_ref() - .map(|url| format!(" <{}>", url)) - .unwrap_or("".to_owned()) - ) - } - GitlabHook::Note(note) => { - if let Some(_) = note.snippet { - return None; - } - if let Some(commit) = ¬e.commit { - format!( - "[{}] {} commented on commit {:?} <{}>", - note.project.name, note.user.name, commit.id, commit.url, - ) - } else if let Some(issue) = ¬e.issue { - format!( - "[{}] {} commented on issue {}: {} <{}>", - note.project.name, - note.user.name, - issue.iid, - issue.title, - note.object_attributes.url, - ) - } else if let Some(mr) = ¬e.merge_request { - format!( - "[{}] {} commented on merge request {}: {} <{}>", - note.project.name, note.user.name, mr.iid, mr.title, note.object_attributes.url, - ) - } else { - unreachable!() - } - } - GitlabHook::Build(build) => { - println!("Build: {:?}", build); - return None; - } - GitlabHook::WikiPage(page) => { - let action = match page.object_attributes.action { - WikiPageAction::Update => "updated", - WikiPageAction::Create => "created", - }; - format!( - "[{}] {} {} wiki page {} <{}>", - page.project.name, - page.user.name, - action, - page.object_attributes.title, - page.object_attributes.url, - ) - } - _glh => { - debug!("Hook not supported"); - return None; - } + } /* + Hook::Issue(issue) => { + let action = match issue.object_attributes.action { + Some(IssueAction::Update) => return None, + Some(IssueAction::Open) => "opened", + Some(IssueAction::Close) => "closed", + Some(IssueAction::Reopen) => "reopened", + None => return None, + }; + format!( + "[{}] {} {} issue {}: {}{}", + issue.project.name, + issue.user.name, + action, + issue.object_attributes.iid, + issue.object_attributes.title, + issue + .object_attributes + .url + .as_ref() + .map(|url| format!(" <{}>", url)) + .unwrap_or("".to_owned()) + ) + } + Hook::MergeRequest(merge_req) => { + let action = match merge_req.object_attributes.action { + Some(MergeRequestAction::Update) => return None, + Some(MergeRequestAction::Open) => "opened", + Some(MergeRequestAction::Close) => "closed", + Some(MergeRequestAction::Reopen) => "reopened", + Some(MergeRequestAction::Merge) => "merged", + None => return None, + _ => { + log::warn!( + "Unsupported merge request action: {:?}", + merge_req.object_attributes.action + ); + return None; + } + }; + format!( + "[{}] {} {} merge request {}: {}{}", + merge_req.project.name, + merge_req.user.name, + action, + merge_req.object_attributes.iid, + merge_req.object_attributes.title, + merge_req + .object_attributes + .url + .as_ref() + .map(|url| format!(" <{}>", url)) + .unwrap_or("".to_owned()) + ) + } + Hook::Note(note) => { + if let Some(_) = note.snippet { + return None; + } + if let Some(commit) = ¬e.commit { + format!( + "[{}] {} commented on commit {:?} <{}>", + note.project.name, note.user.name, commit.id, commit.url, + ) + } else if let Some(issue) = ¬e.issue { + format!( + "[{}] {} commented on issue {}: {} <{}>", + note.project.name, + note.user.name, + issue.iid, + issue.title, + note.object_attributes.url, + ) + } else if let Some(mr) = ¬e.merge_request { + format!( + "[{}] {} commented on merge request {}: {} <{}>", + note.project.name, note.user.name, mr.iid, mr.title, note.object_attributes.url, + ) + } else { + unreachable!() + } + } + Hook::Build(build) => { + println!("Build: {:?}", build); + return None; + } + Hook::WikiPage(page) => { + let action = match page.object_attributes.action { + WikiPageAction::Update => "updated", + WikiPageAction::Create => "created", + }; + format!( + "[{}] {} {} wiki page {} <{}>", + page.project.name, + page.user.name, + action, + page.object_attributes.title, + page.object_attributes.url, + ) + } + _ => { + debug!("Hook not supported"); + return None; + } + */ }) } diff --git a/src/main.rs b/src/main.rs index a054f24..d6d0157 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,7 +91,7 @@ async fn main() -> Result { } } wh = value_rx.recv() => { - if let Some(Hook::Gitlab(hook)) = wh { + if let Some(hook) = wh { client.hook(hook).await } } diff --git a/src/web.rs b/src/web.rs index 9feeabe..5892d66 100644 --- a/src/web.rs +++ b/src/web.rs @@ -68,7 +68,7 @@ async fn hooks_inner(req: Request, secret: &str) -> Result, secret: &str) -> Result { debug!("Passed: {:?}", wh); - - match wh { - hook @ Hook::Gitlab(_) => value_tx.lock().unwrap().send(hook).unwrap(), - _ => (), - } - + value_tx.lock().unwrap().send(wh).unwrap(); Ok(Response::new(Full::new(Bytes::from("Hello, World!")))) } Err(err) => error_res(err),