use moments_entities::{Event, Source, TimelineIcon, TimelineItem, TitleSegment}; use serde_json::Value; const FALLBACK_HOST: &str = "hg-edge.mozilla.org"; pub(crate) fn reshape(event: &Event) -> TimelineItem { let p = &event.payload; let host = p .get("_host") .and_then(Value::as_str) .unwrap_or(FALLBACK_HOST); let repo = p .get("_repo") .and_then(Value::as_str) .unwrap_or("(unknown repo)"); let node = p.get("node").and_then(Value::as_str).unwrap_or(""); let short_node: String = node.chars().take(12).collect(); let desc = p .get("desc") .and_then(Value::as_str) .unwrap_or("") .lines() .next() .unwrap_or("") .to_string(); let author = p .get("author") .and_then(Value::as_str) .map(author_name); let mut title = Vec::new(); if let Some(name) = author { title.push(TitleSegment::text(format!("{name} "))); } title.push(TitleSegment::text("committed ")); title.push(TitleSegment::link( short_node, format!("https://{host}/{repo}/rev/{node}"), )); title.push(TitleSegment::text(" in ")); title.push(TitleSegment::link( repo.to_string(), format!("https://{host}/{repo}"), )); let subtitle = (!desc.is_empty()).then(|| vec![TitleSegment::text(desc)]); TimelineItem { id: event.id.clone(), source: Source::Hg, action: event.action.clone(), occurred_at: event.occurred_at, icon: TimelineIcon::GitCommit, title, subtitle, body: None, } } /// Drop the `` portion of an hg author string ("Name ") and /// trim — leaves just the display name. If there's no email, return the /// trimmed input. fn author_name(s: &str) -> String { if let Some(idx) = s.find('<') { s[..idx].trim().to_string() } else { s.trim().to_string() } } #[cfg(test)] mod tests { use super::*; use chrono::{TimeZone, Utc}; use serde_json::json; fn ev(payload: Value) -> Event { Event { id: "hg:build/puppet:abc".into(), source: Source::Hg, action: "Commit".into(), occurred_at: Utc.with_ymd_and_hms(2018, 5, 1, 12, 0, 0).unwrap(), public: true, payload, } } fn render(item: &TimelineItem) -> String { item.title .iter() .map(|s| match s { TitleSegment::Text { text } => text.clone(), TitleSegment::Link { text, .. } => text.clone(), }) .collect() } #[test] fn reshape_hg_commit() { let raw = json!({ "_host": "hg-edge.mozilla.org", "_repo": "build/puppet", "node": "abcdef1234567890abcdef", "desc": "Bug 1234 - fix something\n\nlonger body", "author": "Rob Thijssen " }); let item = reshape(&ev(raw)); assert_eq!(item.icon, TimelineIcon::GitCommit); let r = render(&item); assert!( r.contains("Rob Thijssen committed abcdef123456 in build/puppet"), "got: {r}" ); assert_eq!( item.subtitle.unwrap(), vec![TitleSegment::text("Bug 1234 - fix something")] ); } #[test] fn drops_email_from_author() { assert_eq!(author_name("Rob Thijssen "), "Rob Thijssen"); assert_eq!(author_name("nobody"), "nobody"); assert_eq!(author_name(" spaced "), "spaced"); } }