use serde_json::json;
pub(crate) const BUILD_VERSION: &str = env!("CARGO_PKG_VERSION");
pub(crate) const BUILD_GIT_SHA: &str = env!("CODELENS_BUILD_GIT_SHA");
pub(crate) const BUILD_TIME: &str = env!("CODELENS_BUILD_TIME");
pub(crate) const BUILD_GIT_DIRTY: &str = env!("CODELENS_BUILD_GIT_DIRTY");
pub(crate) fn build_git_dirty() -> bool {
matches!(BUILD_GIT_DIRTY, "true" | "1" | "yes")
}
fn civil_from_days(days: i64) -> (i64, u64, u64) {
let z = days + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = (z - era * 146_097) as u64;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365;
let y = yoe as i64 + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let year = y + if m <= 2 { 1 } else { 0 };
(year, m, d)
}
fn days_from_civil(year: i64, month: u64, day: u64) -> i64 {
let year = year - if month <= 2 { 1 } else { 0 };
let era = if year >= 0 { year } else { year - 399 } / 400;
let yoe = year - era * 400;
let month_index = month as i64 + if month > 2 { -3 } else { 9 };
let doy = (153 * month_index + 2) / 5 + day as i64 - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
era * 146_097 + doe - 719_468
}
fn format_rfc3339_utc(unix_seconds: u64) -> String {
let days = (unix_seconds / 86_400) as i64;
let secs_in_day = unix_seconds % 86_400;
let hour = secs_in_day / 3600;
let minute = (secs_in_day % 3600) / 60;
let second = secs_in_day % 60;
let (year, month, day) = civil_from_days(days);
format!("{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}Z")
}
fn parse_fixed_u64(value: &[u8]) -> Option<u64> {
std::str::from_utf8(value).ok()?.parse::<u64>().ok()
}
fn parse_fixed_i64(value: &[u8]) -> Option<i64> {
std::str::from_utf8(value).ok()?.parse::<i64>().ok()
}
fn parse_rfc3339_utc_seconds(value: &str) -> Option<u64> {
let bytes = value.as_bytes();
if bytes.len() != 20
|| bytes.get(4) != Some(&b'-')
|| bytes.get(7) != Some(&b'-')
|| bytes.get(10) != Some(&b'T')
|| bytes.get(13) != Some(&b':')
|| bytes.get(16) != Some(&b':')
|| bytes.get(19) != Some(&b'Z')
{
return None;
}
let year = parse_fixed_i64(&bytes[0..4])?;
let month = parse_fixed_u64(&bytes[5..7])?;
let day = parse_fixed_u64(&bytes[8..10])?;
let hour = parse_fixed_u64(&bytes[11..13])?;
let minute = parse_fixed_u64(&bytes[14..16])?;
let second = parse_fixed_u64(&bytes[17..19])?;
if !(1..=12).contains(&month)
|| !(1..=31).contains(&day)
|| hour > 23
|| minute > 59
|| second > 59
{
return None;
}
let days = days_from_civil(year, month, day);
if days < 0 {
return None;
}
Some(days as u64 * 86_400 + hour * 3600 + minute * 60 + second)
}
fn current_executable_path() -> Result<std::path::PathBuf, String> {
if let Some(path) = std::env::var_os("CODELENS_EXECUTABLE_PATH_OVERRIDE") {
return Ok(std::path::PathBuf::from(path));
}
std::env::current_exe().map_err(|err| format!("current_exe unavailable: {err}"))
}
pub(crate) fn daemon_binary_drift_payload(daemon_started_at: &str) -> serde_json::Value {
let daemon_started_seconds = match parse_rfc3339_utc_seconds(daemon_started_at) {
Some(value) => value,
None => {
return json!({
"status": "unknown",
"stale_daemon": false,
"restart_recommended": false,
"reason": "unable to parse daemon_started_at"
});
}
};
let executable_path = match current_executable_path() {
Ok(path) => path,
Err(reason) => {
return json!({
"status": "unknown",
"stale_daemon": false,
"restart_recommended": false,
"reason": reason,
});
}
};
let modified = match std::fs::metadata(&executable_path)
.and_then(|metadata| metadata.modified())
.map_err(|err| format!("unable to inspect executable metadata: {err}"))
{
Ok(value) => value,
Err(reason) => {
return json!({
"status": "unknown",
"stale_daemon": false,
"restart_recommended": false,
"executable_path": executable_path.to_string_lossy(),
"reason": reason,
});
}
};
let modified_seconds = match modified
.duration_since(std::time::UNIX_EPOCH)
.map(|duration| duration.as_secs())
{
Ok(value) => value,
Err(_) => {
return json!({
"status": "unknown",
"stale_daemon": false,
"restart_recommended": false,
"executable_path": executable_path.to_string_lossy(),
"reason": "executable mtime predates unix epoch"
});
}
};
let stale_daemon = modified_seconds > daemon_started_seconds;
let status = if stale_daemon { "stale" } else { "ok" };
let reason_code = if stale_daemon {
Some("stale_daemon_binary")
} else {
None
};
let recommended_action = if stale_daemon {
Some("restart_mcp_server")
} else {
None
};
json!({
"status": status,
"stale_daemon": stale_daemon,
"restart_recommended": stale_daemon,
"reason_code": reason_code,
"recommended_action": recommended_action,
"action_target": if stale_daemon { Some("daemon") } else { None },
"executable_path": executable_path.to_string_lossy(),
"executable_modified_at": format_rfc3339_utc(modified_seconds),
"daemon_started_at": daemon_started_at,
"binary_build_time": BUILD_TIME,
"binary_git_sha": BUILD_GIT_SHA,
"reason": if stale_daemon {
Some("disk executable is newer than the running daemon; restart the MCP server to pick up the latest build")
} else {
None
},
})
}
#[cfg(test)]
mod tests {
use super::{format_rfc3339_utc, parse_rfc3339_utc_seconds};
#[test]
fn rfc3339_utc_round_trips_known_epoch_values() {
let samples = [
(0, "1970-01-01T00:00:00Z"),
(86_400, "1970-01-02T00:00:00Z"),
(1_712_793_600, "2024-04-11T00:00:00Z"),
];
for (unix_seconds, expected) in samples {
assert_eq!(format_rfc3339_utc(unix_seconds), expected);
assert_eq!(parse_rfc3339_utc_seconds(expected), Some(unix_seconds));
}
}
}