[TSM.ID].[11031972] PXE : Platform X Ecosystem I [118 Module -LIVE-]
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "xcu-ouroboros-engine"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["TSM.ID <tsm@tsm.id>"]
|
||||
description = "[TSM.ID].[11031972] xcu-ouroboros-engine"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
@@ -0,0 +1,134 @@
|
||||
//! [TSM.ID].[11031972] -- Platform X Ecosystem
|
||||
//! xcu-ouroboros-engine -- Self-evolving system orchestrator with hot-reload
|
||||
#![deny(warnings)]
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum XcuError {
|
||||
InitFailed(String),
|
||||
InvalidConfig(String),
|
||||
OperationFailed(String),
|
||||
ResourceExhausted,
|
||||
NotFound(String),
|
||||
Timeout,
|
||||
IoError(String),
|
||||
SecurityViolation(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for XcuError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InitFailed(e) => write!(f, "Init failed: {e}"),
|
||||
Self::InvalidConfig(e) => write!(f, "Invalid config: {e}"),
|
||||
Self::OperationFailed(e) => write!(f, "Op failed: {e}"),
|
||||
Self::ResourceExhausted => write!(f, "Resource exhausted"),
|
||||
Self::NotFound(e) => write!(f, "Not found: {e}"),
|
||||
Self::Timeout => write!(f, "Timeout"),
|
||||
Self::IoError(e) => write!(f, "IO error: {e}"),
|
||||
Self::SecurityViolation(e) => write!(f, "Security violation: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::error::Error for XcuError {}
|
||||
pub type Result<T> = std::result::Result<T, XcuError>;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ServiceConfig {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub params: HashMap<String, String>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl ServiceConfig {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self { name: name.to_string(), version: "0.1.0".to_string(), params: HashMap::new(), enabled: true }
|
||||
}
|
||||
pub fn param(&mut self, k: &str, v: &str) -> &mut Self {
|
||||
self.params.insert(k.to_string(), v.to_string()); self
|
||||
}
|
||||
pub fn get_param(&self, k: &str) -> Result<&str> {
|
||||
self.params.get(k).map(|s| s.as_str()).ok_or_else(|| XcuError::NotFound(k.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ServiceState { Created, Initializing, Ready, Running, Degraded, Stopping, Stopped, Failed(String) }
|
||||
|
||||
pub struct Service {
|
||||
config: ServiceConfig,
|
||||
state: Arc<Mutex<ServiceState>>,
|
||||
counters: Arc<Mutex<HashMap<String, u64>>>,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
pub fn new(config: ServiceConfig) -> Result<Self> {
|
||||
if config.name.is_empty() { return Err(XcuError::InvalidConfig("empty name".into())); }
|
||||
Ok(Self {
|
||||
config, state: Arc::new(Mutex::new(ServiceState::Created)),
|
||||
counters: Arc::new(Mutex::new(HashMap::new())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn init(&self) -> Result<()> {
|
||||
let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
|
||||
*s = ServiceState::Initializing;
|
||||
*s = ServiceState::Ready;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Result<()> {
|
||||
let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
|
||||
match &*s {
|
||||
ServiceState::Ready | ServiceState::Stopped => { *s = ServiceState::Running; Ok(()) }
|
||||
other => Err(XcuError::InvalidConfig(format!("Cannot start from {other:?}"))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> Result<()> {
|
||||
let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
|
||||
*s = ServiceState::Stopping;
|
||||
*s = ServiceState::Stopped;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn state(&self) -> Result<ServiceState> {
|
||||
Ok(self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?.clone())
|
||||
}
|
||||
|
||||
pub fn increment(&self, key: &str) -> Result<u64> {
|
||||
let mut c = self.counters.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
|
||||
let v = c.entry(key.to_string()).or_insert(0);
|
||||
*v += 1;
|
||||
Ok(*v)
|
||||
}
|
||||
|
||||
pub fn config(&self) -> &ServiceConfig { &self.config }
|
||||
pub fn name(&self) -> &str { &self.config.name }
|
||||
pub fn version(&self) -> &str { &self.config.version }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_service_lifecycle() {
|
||||
let s = Service::new(ServiceConfig::new("xcu-ouroboros-engine")).unwrap();
|
||||
assert_eq!(s.state().unwrap(), ServiceState::Created);
|
||||
s.init().unwrap();
|
||||
assert_eq!(s.state().unwrap(), ServiceState::Ready);
|
||||
s.start().unwrap();
|
||||
assert_eq!(s.state().unwrap(), ServiceState::Running);
|
||||
s.stop().unwrap();
|
||||
assert_eq!(s.state().unwrap(), ServiceState::Stopped);
|
||||
}
|
||||
#[test]
|
||||
fn test_counter() {
|
||||
let s = Service::new(ServiceConfig::new("xcu-ouroboros-engine")).unwrap();
|
||||
assert_eq!(s.increment("ops").unwrap(), 1);
|
||||
assert_eq!(s.increment("ops").unwrap(), 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user