//! 编译期嵌入的构建信息。 //! //! # 为什么读仓库根的那个文件 //! //! `version.json` 由 `scripts/version.mjs` 从 git 历史算出来,是版本号的唯一来源。 //! 这里用 `include_str!` 把仓库根的那一份**嵌进二进制**: //! //! - 不依赖运行时的文件(装到别人机器上也能读到); //! - `include_str!` 会让 cargo 把它登记成依赖,所以它一变,这个 crate 就会重编—— //! 不需要再手写 `cargo:rerun-if-changed`。 //! //! 二进制里的版本号因此一定与它编译时的那棵源码树一致,不会出现"装包写着 0.1.0、 //! 其实是两周后的代码"。 use serde_json::Value; /// 仓库根 `version.json` 的原文。 const RAW: &str = include_str!("../../../version.json"); /// 一份构建的全部身份信息。 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BuildInfo { pub major: u16, pub minor: u16, pub patch: u16, /// git 提交数,单调递增。 pub build: u16, /// 线路协议版本。 pub protocol: u16, } impl BuildInfo { /// 打包成 `major << 16 | minor << 8 | patch`,与协议里的 `Hello.version` 同一套编码。 pub fn packed(&self) -> u32 { ((self.major as u32) << 16) | (((self.minor as u32) & 0xff) << 8) | ((self.patch as u32) & 0xff) } pub fn version_text(&self) -> String { format!("{}.{}.{}", self.major, self.minor, self.patch) } } /// 解析失败时退回全 0——那会让界面显示"版本未知",比编不过或者编出一个假版本好。 pub fn build_info() -> BuildInfo { let Ok(json) = serde_json::from_str::(RAW) else { return BuildInfo { major: 0, minor: 0, patch: 0, build: 0, protocol: 0, }; }; let field = |name: &str| -> u16 { json.get(name) .and_then(Value::as_u64) .unwrap_or(0) .min(u16::MAX as u64) as u16 }; BuildInfo { major: field("major"), minor: field("minor"), patch: field("patch"), build: field("build"), protocol: field("protocol"), } }