The new Gitea Actions build gate runs `cargo fmt --check`, `clippy -D warnings`, and `cargo test` — stricter than the old deploy.sh, which only `cargo build`d. That surfaced pre-existing drift that never compiled under the test/clippy profile: - apply rustfmt across the workspace (formatting only, no logic changes) - moments-data: add the missing `prune_events` to the test-only `NoopWriter` stub (the EventWriter trait gained it with the blog-prune feature; a plain `cargo build` never compiles the `#[cfg(test)]` stub, so it went stale) - moments-api: `.max().min()` -> `.clamp()`, and build `usvg::Options` with struct-update syntax instead of post-Default field assignment Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7zF7Kf4JqDwa6M8Qgge9M
124 lines
3.5 KiB
Rust
124 lines
3.5 KiB
Rust
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 `<email>` portion of an hg author string ("Name <email>") 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 <rthijssen@mozilla.com>"
|
|
});
|
|
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@example>"), "Rob Thijssen");
|
|
assert_eq!(author_name("nobody"), "nobody");
|
|
assert_eq!(author_name(" spaced "), "spaced");
|
|
}
|
|
}
|