gemachain_version/
lib.rs

1#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(min_specialization))]
2
3extern crate serde_derive;
4use serde_derive::{Deserialize, Serialize};
5use gemachain_sdk::sanitize::Sanitize;
6use std::{convert::TryInto, fmt};
7#[macro_use]
8extern crate gemachain_frozen_abi_macro;
9
10// Older version structure used earlier 1.3.x releases
11#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample)]
12pub struct LegacyVersion {
13    major: u16,
14    minor: u16,
15    patch: u16,
16    commit: Option<u32>, // first 4 bytes of the sha1 commit hash
17}
18
19impl Sanitize for LegacyVersion {}
20
21#[derive(Serialize, Deserialize, Clone, PartialEq, AbiExample)]
22pub struct Version {
23    pub major: u16,
24    pub minor: u16,
25    pub patch: u16,
26    pub commit: Option<u32>, // first 4 bytes of the sha1 commit hash
27    pub feature_set: u32,    // first 4 bytes of the FeatureSet identifier
28}
29
30impl From<LegacyVersion> for Version {
31    fn from(legacy_version: LegacyVersion) -> Self {
32        Self {
33            major: legacy_version.major,
34            minor: legacy_version.minor,
35            patch: legacy_version.patch,
36            commit: legacy_version.commit,
37            feature_set: 0,
38        }
39    }
40}
41
42fn compute_commit(sha1: Option<&'static str>) -> Option<u32> {
43    let sha1 = sha1?;
44    if sha1.len() < 8 {
45        None
46    } else {
47        u32::from_str_radix(&sha1[..8], 16).ok()
48    }
49}
50
51impl Default for Version {
52    fn default() -> Self {
53        let feature_set = u32::from_le_bytes(
54            gemachain_sdk::feature_set::ID.as_ref()[..4]
55                .try_into()
56                .unwrap(),
57        );
58        Self {
59            major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
60            minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
61            patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
62            commit: compute_commit(option_env!("CI_COMMIT")),
63            feature_set,
64        }
65    }
66}
67
68impl fmt::Display for Version {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "{}.{}.{}", self.major, self.minor, self.patch,)
71    }
72}
73
74impl fmt::Debug for Version {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(
77            f,
78            "{}.{}.{} (src:{}; feat:{})",
79            self.major,
80            self.minor,
81            self.patch,
82            match self.commit {
83                None => "devbuild".to_string(),
84                Some(commit) => format!("{:08x}", commit),
85            },
86            self.feature_set,
87        )
88    }
89}
90
91impl Sanitize for Version {}
92
93#[macro_export]
94macro_rules! semver {
95    () => {
96        &*format!("{}", $crate::Version::default())
97    };
98}
99
100#[macro_export]
101macro_rules! version {
102    () => {
103        &*format!("{:?}", $crate::Version::default())
104    };
105}
106
107#[cfg(test)]
108mod test {
109    use super::*;
110
111    #[test]
112    fn test_compute_commit() {
113        assert_eq!(compute_commit(None), None);
114        assert_eq!(compute_commit(Some("1234567890")), Some(0x1234_5678));
115        assert_eq!(compute_commit(Some("HEAD")), None);
116        assert_eq!(compute_commit(Some("garbagein")), None);
117    }
118}