ax_core/util/
version.rs

1use crate::DATABANK_VERSION;
2use lazy_static::lazy_static;
3use serde::{Deserialize, Serialize};
4use std::{
5    env::consts::{ARCH, OS},
6    str::FromStr,
7    sync::OnceLock,
8};
9
10// The hash is provided by GitHub actions, for more information, see:
11// https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
12// It should work with workflow_dispatch as well as push and pull_request events
13// https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
14const GIT_HASH: &str = match option_env!("GITHUB_SHA") {
15    Some(hash) => hash,
16    // This is for cargo installations and builds
17    None => "cargo",
18};
19
20#[cfg(debug_assertions)]
21const PROFILE: &str = "debug";
22#[cfg(not(debug_assertions))]
23const PROFILE: &str = "release";
24
25lazy_static! {
26    pub static ref VERSION: String = NodeVersion::get().to_string();
27}
28
29/// The `OnceLock` allows us to defer evaluating the version to whenever we actually need it, allowing us to modify it in the process.
30///
31/// When used in AX, we use this feature to "monkey-patch" the version to include the AX patch version.
32pub static NODE_VERSION: OnceLock<NodeVersion> = OnceLock::new();
33
34// NOTE: This can be replaced with the `semver` crate
35#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
36pub struct Version {
37    major: u8,
38    minor: u8,
39    patch: u8,
40}
41
42impl Version {
43    pub fn new(major: u8, minor: u8, patch: u8) -> Self {
44        Self { major, minor, patch }
45    }
46}
47
48impl FromStr for Version {
49    type Err = ();
50
51    fn from_str(s: &str) -> Result<Self, Self::Err> {
52        let s = if let Some(pos) = s.find('_') {
53            s.split_at(pos).0
54        } else {
55            s
56        };
57        let mut parts = s.split('.');
58        let major = parts.next().ok_or(())?.parse().map_err(|_| ())?;
59        let minor = parts.next().ok_or(())?.parse().map_err(|_| ())?;
60        let patch = parts.next().ok_or(())?.parse().map_err(|_| ())?;
61        Ok(Self { major, minor, patch })
62    }
63}
64
65impl std::fmt::Display for Version {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
68    }
69}
70
71// *May* be able to replace this structure with a single string using `const_format`
72#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
73#[serde(rename_all = "camelCase")]
74pub struct NodeVersion {
75    pub profile: String,
76    pub target: String,
77    pub version: String,
78    pub git_hash: String,
79}
80
81impl NodeVersion {
82    /// Returns the current node version, associated with the `DATABANK_VERSION` constant.
83    pub fn get() -> &'static NodeVersion {
84        NODE_VERSION.get_or_init(|| NodeVersion {
85            profile: PROFILE.to_string(),
86            target: format!("{}-{}", OS, ARCH),
87            version: format!("{}.0", DATABANK_VERSION),
88            git_hash: GIT_HASH.to_string(),
89        })
90    }
91
92    pub fn version(&self) -> Option<Version> {
93        self.version.parse().ok()
94    }
95}
96
97impl std::fmt::Display for NodeVersion {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        f.write_fmt(format_args!(
100            "{}-{}-{}-{}",
101            self.version, self.git_hash, self.target, self.profile
102        ))
103    }
104}
105
106impl FromStr for NodeVersion {
107    type Err = ();
108
109    fn from_str(s: &str) -> Result<Self, Self::Err> {
110        let mut parts = s.split('-');
111        let version = parts.next().ok_or(())?.to_string();
112        let git_hash = parts.next().ok_or(())?.to_string();
113        let os = parts.next().ok_or(())?;
114        let arch = parts.next().ok_or(())?;
115        let target = format!("{}-{}", os, arch);
116        let profile = parts.next().ok_or(())?.to_string();
117        Ok(Self {
118            version,
119            git_hash,
120            target,
121            profile,
122        })
123    }
124}