xaeroflux_core/
lib.rs

1//! Core initialization and utilities for xaeroflux.
2//!
3//! This module provides:
4//! - Global configuration loading and access (`load_config`, `CONF`).
5//! - Initialization of global thread pools for dispatch and I/O.
6//! - Serialization and deserialization helpers for rkyv.
7//! - Application startup (`initialize`) with logging and banner.
8pub mod config;
9pub mod date_time;
10pub mod event;
11pub mod hash;
12pub mod keys;
13pub mod listeners;
14pub mod logs;
15pub mod size;
16pub mod sys;
17
18use std::{any::Any, env, fmt::Debug, sync::OnceLock};
19
20use figlet_rs::FIGfont;
21use rkyv::{
22    Archive,
23    bytecheck::CheckBytes,
24    de::Pool,
25    rancor::{Failure, Strategy},
26    util::AlignedVec,
27    validation::{Validator, archive::ArchiveValidator, shared::SharedValidator},
28};
29use threadpool::ThreadPool;
30use tracing::info;
31
32use crate as xaeroflux_core;
33use crate::{config::Config, logs::init_logging};
34
35/// Marker trait for types that can be stored as xaeroflux events.
36///
37/// Requirements:
38/// - `Any` for downcasting support.
39/// - `Send + Sync` for safe cross-thread use.
40/// - `Clone` for duplicating payloads.
41/// - `Debug` for logging and diagnostics.
42pub trait XaeroData: Any + Send + Sync + Clone + Debug {}
43
44impl<T> XaeroData for T where T: Any + Send + Sync + Clone + Debug {}
45
46/// Global, singleton configuration instance.
47///
48/// Initialized by `load_config` and reused thereafter.
49pub static CONF: OnceLock<config::Config> = OnceLock::new();
50
51/// Global thread pool for dispatching work to worker threads.
52pub static DISPATCHER_POOL: OnceLock<ThreadPool> = OnceLock::new();
53
54/// Global thread pool for performing I/O-bound tasks.
55pub static IO_POOL: OnceLock<ThreadPool> = OnceLock::new();
56
57/// Global runtime for peer-to-peer networking tasks.
58pub static P2P_RUNTIME: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
59
60/// Initializes the global P2P Tokio runtime.
61///
62/// Uses the `threads.num_worker_threads` setting from configuration,
63/// defaulting to at least one thread, and stores it in `P2P_RUNTIME`.
64pub fn init_p2p_runtime() -> &'static tokio::runtime::Runtime {
65    P2P_RUNTIME.get_or_init(|| {
66        let conf = CONF.get_or_init(Config::default);
67        let threads = conf.threads.num_worker_threads.max(2);
68        tokio::runtime::Builder::new_multi_thread()
69            .worker_threads(threads)
70            .enable_all()
71            .thread_name("xaeroflux-p2p")
72            .build()
73            .expect("Failed to create P2P runtime")
74    })
75}
76
77/// Initializes the global dispatcher thread pool.
78///
79/// Uses the `threads.num_worker_threads` setting from configuration,
80/// defaulting to at least one thread, and stores it in `DISPATCHER_POOL`.
81pub fn init_global_dispatcher_pool() {
82    DISPATCHER_POOL.get_or_init(|| {
83        let conf = CONF.get_or_init(Config::default);
84        let no_of_worker_threads = conf.threads.num_worker_threads.max(1);
85
86        ThreadPool::new(no_of_worker_threads)
87    });
88}
89
90/// Initializes the global I/O thread pool.
91///
92/// Uses the `threads.num_io_threads` setting from configuration,
93/// defaulting to at least one thread, and stores it in `IO_POOL`.
94pub fn init_global_io_pool() {
95    IO_POOL.get_or_init(|| {
96        let conf = CONF.get_or_init(Config::default);
97        let num_io_threads = conf.threads.num_io_threads.max(1);
98
99        ThreadPool::new(num_io_threads)
100    });
101}
102
103/// Perform global initialization of xaeroflux core.
104///
105/// - Loads and validates configuration (`xaeroflux.toml`).
106/// - Initializes dispatcher and I/O thread pools.
107/// - Sets up logging and displays startup banner.
108///
109/// # Panics
110/// Will panic if the configuration name is not "xaeroflux".
111pub fn initialize() {
112    #[cfg(not(test))]
113    xaeroflux_core::size::init(); // Initialize the size module
114    xaeroflux_core::size::init();
115    let project_root = env!("CARGO_MANIFEST_DIR");
116    let cfg_path = format!("{}/xaeroflux.toml", project_root);
117    unsafe { env::set_var("XAERO_CONFIG", &cfg_path) };
118    let config = load_config();
119    if config.name != "xaeroflux" {
120        panic!("Invalid config file. Expected 'xaeroflux'.");
121    }
122    init_global_dispatcher_pool();
123    init_global_io_pool();
124    init_logging(); // Initialize the logging system
125    show_banner();
126    info!("XaeroFlux initialized");
127}
128
129/// Serialize a data payload into a zero-copy `AlignedVec`.
130///
131/// Uses the `rkyv` framework with the `rancor` sharing strategy.
132///
133/// # Errors
134/// Returns `Failure` if serialization fails.
135pub fn serialize<T>(data: &T) -> Result<AlignedVec, Failure>
136where
137    T: XaeroData
138        + for<'a> rkyv::Serialize<
139            rkyv::rancor::Strategy<
140                rkyv::ser::Serializer<
141                    rkyv::util::AlignedVec,
142                    rkyv::ser::allocator::ArenaHandle<'a>,
143                    rkyv::ser::sharing::Share,
144                >,
145                rkyv::rancor::Failure,
146            >,
147        >,
148{
149    rkyv::to_bytes::<Failure>(data)
150}
151
152/// Deserialize bytes back into a data type.
153///
154/// Uses `rkyv` zero-copy deserialization with validation.
155///
156/// # Errors
157/// Returns `Failure` if validation or deserialization fails.
158pub fn deserialize<T>(data: &[u8]) -> Result<T, Failure>
159where
160    T: XaeroData + rkyv::Archive,
161    for<'a> <T as Archive>::Archived: CheckBytes<
162        Strategy<Validator<ArchiveValidator<'a>, SharedValidator>, rkyv::rancor::Failure>,
163    >,
164    <T as Archive>::Archived: rkyv::Deserialize<T, Strategy<Pool, Failure>>,
165{
166    rkyv::from_bytes::<T, Failure>(data)
167}
168/// Load or retrieve the global configuration.
169///
170/// Reads the `XAERO_CONFIG` environment variable or defaults to
171/// `xaeroflux.toml` in the project root, parses it via `toml`.
172///
173/// # Panics
174/// Will panic if the file cannot be read or parsed.
175pub fn load_config() -> &'static config::Config {
176    CONF.get_or_init(|| {
177        let path = std::env::var("XAERO_CONFIG").unwrap_or_else(|_| "xaeroflux.toml".into());
178        let s = std::fs::read_to_string(path).expect("read config");
179        toml::from_str(&s).expect("parse config")
180    })
181}
182
183/// Display the ASCII art banner for xaeroflux startup.
184///
185/// Uses FIGfont to render "XAER0FLUX v.{version}" and logs it.
186pub fn show_banner() {
187    info!("XaeroFlux initializing...");
188    let slant = FIGfont::standard().expect("load slant font");
189    let v = env!("CARGO_PKG_VERSION");
190    let x = format!("XAER0FLUX v. {v}");
191    let s = x.as_str();
192    let figure = slant.convert(s).expect("convert text");
193    tracing::info!("\n{}", figure);
194}
195#[cfg(test)]
196mod tests {
197    use rkyv::{Archive, Deserialize, Serialize, rancor::Failure};
198    use xaeroflux_core::event;
199
200    use super::*;
201
202    #[test]
203    fn test_initialize() {
204        initialize();
205        assert!(CONF.get().is_some());
206        assert_eq!(CONF.get().expect("failed to load config").name, "xaeroflux");
207        assert_eq!(CONF.get().expect("failed to load config").version, 1_u64);
208    }
209
210    #[test]
211    fn test_load_config() {
212        initialize();
213        let config = load_config();
214        assert_eq!(config.name, "xaeroflux");
215        assert_eq!(config.version, 1_u64);
216    }
217    #[test]
218    fn test_xaero_data() {
219        initialize();
220        #[derive(Archive, Serialize, Deserialize, Debug, Clone, Default)]
221        struct TestData {
222            id: u32,
223            name: String,
224        }
225        // No explicit implementation needed as the blanket implementation covers this.
226        let data = TestData {
227            id: 1,
228            name: "Test".to_string(),
229        };
230        assert_eq!(data.id, 1);
231        assert_eq!(data.name, "Test");
232        // serialize
233        let d = rkyv::to_bytes::<Failure>(&data).expect("failed to serialize");
234        assert!(!d.is_empty());
235    }
236    #[test]
237    fn test_event_type() {
238        initialize();
239        let e = event::EventType::SystemEvent(event::SystemEventKind::Start);
240        let event = event::EventType::from_u8(1);
241        assert_eq!(event, e);
242    }
243    #[test]
244    fn test_event() {
245        initialize();
246        let data = event::EventType::from_u8(0);
247        let event = event::Event::<event::EventType>::new(data.clone(), 0);
248        assert_eq!(event.event_type, data);
249        assert_eq!(
250            event.version,
251            CONF.get().expect("failed to load config").version
252        );
253    }
254}