byteor_core/
lib.rs

1#![deny(missing_docs)]
2#![deny(unreachable_pub, rust_2018_idioms)]
3#![forbid(unsafe_code)]
4
5//! Enterprise "core" primitives.
6//!
7//! This crate centralizes stable identifiers and default path conventions shared across
8//! enterprise binaries (runtime + product apps).
9
10/// Configuration primitives shared across enterprise binaries.
11pub mod config {
12    /// Deployment environment.
13    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
14    pub enum Environment {
15        /// Developer workstation / local iteration.
16        Dev,
17        /// Pre-production / validation environment.
18        Staging,
19        /// Production environment.
20        Prod,
21    }
22
23    impl Environment {
24        /// Parse an environment from a CLI/env string.
25        pub fn parse(s: &str) -> Option<Self> {
26            match s {
27                "dev" => Some(Self::Dev),
28                "staging" => Some(Self::Staging),
29                "prod" => Some(Self::Prod),
30                _ => None,
31            }
32        }
33
34        /// Stable identifier used in logs and audit artifacts.
35        pub const fn as_str(&self) -> &'static str {
36            match self {
37                Environment::Dev => "dev",
38                Environment::Staging => "staging",
39                Environment::Prod => "prod",
40            }
41        }
42    }
43}
44
45/// Product identity.
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum Product {
48    /// The operator CLI runtime binary.
49    Runtime,
50    /// The EdgePlane product.
51    Edgeplane,
52    /// The ActionGraph product.
53    Actiongraph,
54    /// The DataGuard product.
55    Dataguard,
56}
57
58impl Product {
59    /// Stable identifier used in filenames and logs.
60    pub const fn id(&self) -> &'static str {
61        match self {
62            Product::Runtime => "runtime",
63            Product::Edgeplane => "edgeplane",
64            Product::Actiongraph => "actiongraph",
65            Product::Dataguard => "dataguard",
66        }
67    }
68}
69
70/// Default paths and env var conventions.
71pub mod paths {
72    use super::Product;
73    use std::path::{Path, PathBuf};
74
75    /// Env var naming the shared-memory parent directory.
76    pub const ENV_SHM_PARENT: &str = "SHM_PARENT";
77    /// Env var naming the default lane-path mapping file.
78    pub const ENV_LANE_PATH: &str = "BYTEOR_LANE_PATH";
79    /// Env var naming the default spec path (packaging convention).
80    pub const ENV_SPEC: &str = "BYTEOR_SPEC";
81    /// Env var naming the default wait preset (packaging convention).
82    pub const ENV_WAIT: &str = "BYTEOR_WAIT";
83
84    /// Default SHM parent used when `SHM_PARENT` is not set.
85    pub const DEFAULT_SHM_PARENT: &str = "/dev/shm";
86    /// Default runtime directory used by systemd packaging scaffolds.
87    pub const DEFAULT_RUN_DIR: &str = "/run/byteor";
88    /// Default spec path used by packaging scaffolds.
89    pub const DEFAULT_SPEC_PATH: &str = "/etc/byteor/spec.kv";
90
91    /// Read `SHM_PARENT` with a default fallback.
92    pub fn shm_parent_from_env() -> PathBuf {
93        std::env::var(ENV_SHM_PARENT)
94            .ok()
95            .map(PathBuf::from)
96            .unwrap_or_else(|| PathBuf::from(DEFAULT_SHM_PARENT))
97    }
98
99    /// Read `BYTEOR_LANE_PATH` if set.
100    pub fn lane_path_from_env() -> Option<PathBuf> {
101        std::env::var(ENV_LANE_PATH).ok().map(PathBuf::from)
102    }
103
104    /// Deterministic lane-path used when a dedicated runtime directory is available.
105    ///
106    /// Note: the `Runtime` product intentionally uses the packaging filename
107    /// `single_ring.shm` for compatibility with the scaffold templates.
108    pub fn service_single_ring_lane_path(product: Product) -> PathBuf {
109        let run_dir = Path::new(DEFAULT_RUN_DIR);
110        match product {
111            Product::Runtime => run_dir.join("single_ring.shm"),
112            other => run_dir.join(format!("{}-single_ring.shm", other.id())),
113        }
114    }
115
116    fn try_prepare_run_dir() -> Option<PathBuf> {
117        let run_dir = PathBuf::from(DEFAULT_RUN_DIR);
118        if run_dir.exists() {
119            if run_dir.is_dir() {
120                return Some(run_dir);
121            }
122            return None;
123        }
124
125        if std::fs::create_dir_all(&run_dir).is_ok() {
126            Some(run_dir)
127        } else {
128            None
129        }
130    }
131
132    /// Compute a default lane path for `run` commands.
133    ///
134    /// Behavior:
135    /// - If a lane-path is provided by CLI, use it.
136    /// - Else if `BYTEOR_LANE_PATH` is set, use it.
137    /// - Else if the system runtime dir exists/is creatable, use the deterministic service path.
138    /// - Else fall back to a PID-suffixed path under `shm_parent` (avoids collisions in dev).
139    pub fn default_lane_path_for_run(
140        product: Product,
141        cli_lane_path: Option<PathBuf>,
142        shm_parent: &Path,
143    ) -> PathBuf {
144        if let Some(p) = cli_lane_path {
145            return p;
146        }
147        if let Some(p) = lane_path_from_env() {
148            return p;
149        }
150        if try_prepare_run_dir().is_some() {
151            return service_single_ring_lane_path(product);
152        }
153        shm_parent.join(format!(
154            "byteor-{}-single-ring-{}.shm",
155            product.id(),
156            std::process::id()
157        ))
158    }
159
160    #[cfg(test)]
161    mod tests {
162        use super::*;
163
164        #[test]
165        fn service_paths_are_deterministic_and_product_scoped() {
166            assert_eq!(
167                service_single_ring_lane_path(Product::Runtime),
168                PathBuf::from("/run/byteor/single_ring.shm")
169            );
170            assert_eq!(
171                service_single_ring_lane_path(Product::Edgeplane),
172                PathBuf::from("/run/byteor/edgeplane-single_ring.shm")
173            );
174        }
175    }
176}