diff --git a/hive-ag3nt/src/forge_notify.rs b/hive-ag3nt/src/forge_notify.rs index 048b718..790c204 100644 --- a/hive-ag3nt/src/forge_notify.rs +++ b/hive-ag3nt/src/forge_notify.rs @@ -105,6 +105,18 @@ async fn fetch_json( resp.json().await.ok() } +/// Map a Forgejo notification `subject.type` to a human-readable label. +/// Known values: "Pull", "Issue", "Commit", "Repository". Any unknown +/// type is passed through as-is so new Forgejo types degrade gracefully +/// rather than silently collapsing into a generic label. +fn notif_type_label(t: &str) -> &str { + match t { + "Pull" => "PR", + "Issue" => "issue", + other => other, + } +} + /// Truncate a string to `max` bytes at a char boundary, appending `…` if cut. fn truncate(s: &str, max: usize) -> String { if s.len() <= max { @@ -158,12 +170,7 @@ async fn format_notification( .unwrap_or("") .trim(); - // Forgejo API returns "Pull" / "Issue", never "Pull Request". - let kind = match notif_type { - "Pull" => "comment on PR", - "Issue" => "comment on issue", - _ => "comment", - }; + let kind = format!("comment on {}", notif_type_label(notif_type)); let mut out = format!( "[{kind}] {title}\nrepo: {repo}\nurl: {}\n\n{author}: {}\n", if comment_html_url.is_empty() { html_url } else { comment_html_url }, @@ -184,13 +191,11 @@ async fn format_notification( // - Forgejo API type is "Pull" / "Issue", never "Pull Request". let notif_state = notif["subject"]["state"].as_str().unwrap_or(""); - let kind = match (notif_type, notif_state) { - ("Pull", "merged") => "PR merged".to_owned(), - ("Pull", "closed") => "PR closed".to_owned(), - ("Pull", _) => "new PR".to_owned(), - ("Issue", "closed") => "issue closed".to_owned(), - ("Issue", _) => "new issue".to_owned(), - _ => format!("new {notif_type}"), + let label = notif_type_label(notif_type); + let kind = match notif_state { + "merged" => format!("{label} merged"), + "closed" => format!("{label} closed"), + _ => format!("new {label}"), }; // Fetch subject only for body/author on new (open) items — not