pub trait GitlabEventExt { fn project(&self) -> &Project; fn r#ref(&self) -> &str; fn user(&self) -> &str; fn url(&self) -> String; fn title(&self) -> String; } #[derive(Debug, Deserialize)] pub struct Project { pub id: u64, pub name: String, pub description: String, pub web_url: String, pub avatar_url: Option, pub git_ssh_url: String, pub git_http_url: String, pub namespace: String, pub visibility_level: u32, pub path_with_namespace: String, pub default_branch: String, pub homepage: String, pub url: String, pub ssh_url: String, pub http_url: String, } #[derive(Debug, Deserialize)] pub struct Repository { pub name: String, pub url: String, pub description: String, pub homepage: String, pub git_http_url: String, pub git_ssh_url: String, pub visibility_level: u32, } #[derive(Debug, Deserialize)] pub struct Commit { pub id: String, pub title: String, pub url: String, } #[derive(Debug, Deserialize)] #[serde(tag = "object_kind")] pub enum GitlabEvent { #[serde(rename = "push")] Push { event_name: String, before: String, after: String, r#ref: String, ref_protected: bool, checkout_sha: String, user_id: u64, user_name: String, user_username: String, user_email: String, user_avatar: Option, project_id: u64, project: Project, repository: Repository, commits: Vec, total_commits_count: u64, }, #[serde(rename = "tag_push")] TagPush { event_name: String, before: String, after: String, r#ref: String, ref_protected: bool, checkout_sha: String, user_id: u64, user_name: String, user_avatar: Option, project_id: u64, project: Project, repository: Repository, commits: Vec, total_commits_count: u64, }, } impl GitlabEventExt for GitlabEvent { fn project(&self) -> &Project { match self { GitlabEvent::Push { project, .. } => &project, GitlabEvent::TagPush { project, .. } => &project, } } fn r#ref(&self) -> &str { match self { GitlabEvent::Push { r#ref, .. } => &r#ref, GitlabEvent::TagPush { r#ref, .. } => &r#ref, } } fn user(&self) -> &str { match self { GitlabEvent::Push { user_name, .. } => &user_name, GitlabEvent::TagPush { user_name, .. } => &user_name, } } fn url(&self) -> String { let url = match self { GitlabEvent::Push { after, project, .. } => format!("{}/-/commits/{}", project.web_url, after), GitlabEvent::TagPush { r#ref, checkout_sha, project, .. } => { let refname = r#ref.split('/').into_iter().last().unwrap_or(checkout_sha); format!("{}/-/tags/{}", project.web_url, refname) } }; url.replace("http://", "https://").to_string() } fn title(&self) -> String { fn find_commit<'a>(commits: &'a Vec, sha: &str) -> Option<&'a Commit> { commits.iter().find(|commit| commit.id == sha) } match self { GitlabEvent::Push { after, commits, .. } => find_commit(commits, &after) .map(|commit| commit.title.clone()) .unwrap_or_else(|| "New commit(s) pushed".to_string()), GitlabEvent::TagPush { checkout_sha, commits, .. } => find_commit(commits, &checkout_sha) .map(|commit| commit.title.clone()) .unwrap_or_else(|| "New tag pushed".to_string()), } } } #[cfg(test)] mod test { use super::*; use std::{fs::File, io::BufReader}; fn load_test_data(name: &str) -> anyhow::Result { let f = File::open(&format!("{}/test-data/{}.json", env!("CARGO_MANIFEST_DIR"), name))?; let r = BufReader::new(f); let event: GitlabEvent = serde_json::from_reader(r)?; Ok(event) } #[test] pub fn parse_push_event() -> anyhow::Result<()> { let event = load_test_data("push-event")?; match event { GitlabEvent::Push { event_name, before, after, r#ref, checkout_sha, user_username, project, repository, total_commits_count, .. } => { assert_eq!(event_name, "push"); assert_eq!(before, "95790bf891e76fee5e1747ab589903a6a1f80f22"); assert_eq!(after, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7"); assert_eq!(r#ref, "refs/heads/master"); assert_eq!(checkout_sha, "da1560886d4f094c3e6c9ef40349f7d38b5d27d7"); assert_eq!(user_username, "jsmith"); assert_eq!(project.name, "Diaspora"); assert_eq!(project.namespace, "Mike"); assert_eq!(repository.name, "Diaspora"); assert_eq!(repository.url, "git@example.com:mike/diaspora.git"); assert_eq!(total_commits_count, 4); } _ => panic!("not a push event"), }; Ok(()) } #[test] pub fn parse_tag_push_event() -> anyhow::Result<()> { let event = load_test_data("tag-push-event")?; match event { GitlabEvent::TagPush { event_name, before, after, r#ref, checkout_sha, user_name, project, repository, total_commits_count, .. } => { assert_eq!(event_name, "tag_push"); assert_eq!(before, "0000000000000000000000000000000000000000"); assert_eq!(after, "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7"); assert_eq!(r#ref, "refs/tags/v1.0.0"); assert_eq!(checkout_sha, "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7"); assert_eq!(user_name, "John Smith"); assert_eq!(project.name, "Example"); assert_eq!(project.namespace, "Jsmith"); assert_eq!(repository.name, "Example"); assert_eq!(total_commits_count, 0); } _ => panic!("not a tag push event"), }; Ok(()) } }