Skip to main content

winx_code_agent/
build_info.rs

1//! Compile-time build identity exposed consistently by logs, MCP, and doctor.
2
3use serde::{Deserialize, Serialize};
4
5/// Owned build metadata suitable for process boundaries and persisted reports.
6///
7/// Keeping this as a typed value prevents the HTTP adapter, control daemon, and
8/// guardians from each inventing a subtly different version payload.
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
10pub struct BuildIdentity {
11    pub package_version: String,
12    pub display: String,
13    pub revision: String,
14    pub dirty: bool,
15}
16
17impl BuildIdentity {
18    /// Capture the identity compiled into the current executable.
19    pub fn current() -> Self {
20        Self {
21            package_version: package_version().to_string(),
22            display: display_version().to_string(),
23            revision: revision().to_string(),
24            dirty: dirty(),
25        }
26    }
27
28    /// Whether two cooperating processes were built from the same source state.
29    pub fn is_compatible_build(&self, other: &Self) -> bool {
30        self.display == other.display
31    }
32}
33
34/// Cargo package version.
35pub const fn package_version() -> &'static str {
36    env!("CARGO_PKG_VERSION")
37}
38
39/// Source revision captured by `build.rs`, or `package` outside a Git checkout.
40pub const fn revision() -> &'static str {
41    env!("WINX_BUILD_REVISION")
42}
43
44/// Whether tracked source files differed from the captured revision at build time.
45pub fn dirty() -> bool {
46    env!("WINX_BUILD_DIRTY") == "true"
47}
48
49/// Unambiguous version string advertised over MCP and in startup logs.
50pub const fn display_version() -> &'static str {
51    env!("WINX_BUILD_IDENTITY")
52}
53
54#[cfg(test)]
55mod tests {
56    #[test]
57    fn display_identity_contains_package_and_revision() {
58        let identity = super::display_version();
59        assert!(identity.starts_with(super::package_version()));
60        assert!(identity.contains(super::revision()));
61    }
62
63    #[test]
64    fn owned_identity_matches_compile_time_values() {
65        let identity = super::BuildIdentity::current();
66        assert_eq!(identity.package_version, super::package_version());
67        assert_eq!(identity.display, super::display_version());
68        assert_eq!(identity.revision, super::revision());
69        assert_eq!(identity.dirty, super::dirty());
70        assert!(identity.is_compatible_build(&identity));
71    }
72}