use std::collections::BTreeMap;
use std::process::Command;
use std::sync::OnceLock;
use crate::logger::{BoxError, Context, Level, LogRecord, LogValue, Processor};
static HG_INFO_CACHE: OnceLock<Context> = OnceLock::new();
pub struct Mercurial {
min_level: Level,
}
impl Mercurial {
pub fn new(min_level: Level) -> Self {
Self { min_level }
}
}
impl Default for Mercurial {
fn default() -> Self {
Self::new(Level::Debug)
}
}
impl Processor for Mercurial {
fn process(&self, mut record: LogRecord) -> Result<LogRecord, BoxError> {
if record.level < self.min_level {
return Ok(record);
}
let info = HG_INFO_CACHE.get_or_init(mercurial_info).clone();
record.extra.insert("hg".to_string(), LogValue::Map(info));
Ok(record)
}
}
fn mercurial_info() -> Context {
let output = Command::new("hg").args(["id", "-nb"]).output();
let Ok(output) = output else {
return BTreeMap::new();
};
if !output.status.success() {
return BTreeMap::new();
}
let stdout = String::from_utf8_lossy(&output.stdout);
parse_hg_output(stdout.trim()).unwrap_or_default()
}
fn parse_hg_output(output: &str) -> Option<Context> {
let parts: Vec<&str> = output.split_whitespace().collect();
match parts.as_slice() {
[revision, branch] => {
let mut info = BTreeMap::new();
info.insert(
"branch".to_string(),
LogValue::String((*branch).to_string()),
);
info.insert(
"revision".to_string(),
LogValue::String((*revision).to_string()),
);
Some(info)
}
[_, branch, revision, ..] => {
let mut info = BTreeMap::new();
info.insert(
"branch".to_string(),
LogValue::String((*branch).to_string()),
);
info.insert(
"revision".to_string(),
LogValue::String((*revision).to_string()),
);
Some(info)
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use crate::logger::LogValue;
use super::*;
#[test]
fn parses_two_token_hg_output() {
let parsed = parse_hg_output("abc123 default").expect("output should parse");
assert!(matches!(
parsed.get("branch"),
Some(LogValue::String(value)) if value == "default"
));
assert!(matches!(
parsed.get("revision"),
Some(LogValue::String(value)) if value == "abc123"
));
}
#[test]
fn parses_three_token_hg_output() {
let parsed = parse_hg_output("123 default 456").expect("output should parse");
assert!(matches!(
parsed.get("revision"),
Some(LogValue::String(value)) if value == "456"
));
}
}