use std::any::Any;
use std::collections::HashMap;
use std::fmt;
use super::config::{EcosystemConfig, ModuleConfig};
use super::zero_copy::{SharedArrayView, SharedArrayViewMut};
use crate::error::{CoreError, CoreResult, ErrorContext, ErrorLocation};
pub trait Identifiable {
fn id(&self) -> &str;
fn name(&self) -> &str;
fn component_type(&self) -> &str;
fn metadata(&self) -> HashMap<String, String> {
HashMap::new()
}
}
pub trait Configurable {
fn configure(&mut self, config: &EcosystemConfig) -> CoreResult<()>;
fn configure_module(&mut self, config: &ModuleConfig) -> CoreResult<()>;
fn get_config(&self) -> HashMap<String, String>;
fn reset_config(&mut self);
fn validate_config(&self) -> CoreResult<()>;
}
pub trait DataProvider<T> {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn view(&self) -> CoreResult<SharedArrayView<'_, T>>;
fn shape(&self) -> &[usize];
fn dtype(&self) -> &str;
fn is_contiguous(&self) -> bool;
}
pub trait DataConsumer<T> {
fn consume<P: DataProvider<T>>(&mut self, provider: &P) -> CoreResult<()>;
fn consume_slice(&mut self, data: &[T]) -> CoreResult<()>;
fn consume_view(&mut self, view: SharedArrayView<'_, T>) -> CoreResult<()>;
fn expected_shape(&self) -> Option<&[usize]>;
fn can_accept_shape(&self, shape: &[usize]) -> bool;
}
pub trait MutableDataProvider<T>: DataProvider<T> {
fn view_mut(&mut self) -> CoreResult<SharedArrayViewMut<'_, T>>;
fn apply<F>(&mut self, f: F) -> CoreResult<()>
where
F: Fn(&mut T);
fn clear(&mut self);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Capability {
Parallel,
GpuAcceleration,
Simd,
Streaming,
Distributed,
Serializable,
Checkpointing,
ThreadSafe,
Async,
MemoryMapped,
ZeroCopy,
Batched,
Custom(&'static str),
}
impl fmt::Display for Capability {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Capability::Parallel => write!(f, "parallel"),
Capability::GpuAcceleration => write!(f, "gpu"),
Capability::Simd => write!(f, "simd"),
Capability::Streaming => write!(f, "streaming"),
Capability::Distributed => write!(f, "distributed"),
Capability::Serializable => write!(f, "serializable"),
Capability::Checkpointing => write!(f, "checkpointing"),
Capability::ThreadSafe => write!(f, "thread-safe"),
Capability::Async => write!(f, "async"),
Capability::MemoryMapped => write!(f, "mmap"),
Capability::ZeroCopy => write!(f, "zero-copy"),
Capability::Batched => write!(f, "batched"),
Capability::Custom(name) => write!(f, "custom:{name}"),
}
}
}
pub trait ModuleCapability {
fn capabilities(&self) -> Vec<Capability>;
fn has_capability(&self, cap: Capability) -> bool {
self.capabilities().contains(&cap)
}
fn required_capabilities(&self, operation: &str) -> Vec<Capability>;
fn can_perform(&self, operation: &str) -> bool {
let required = self.required_capabilities(operation);
required.iter().all(|cap| self.has_capability(*cap))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SerializationFormat {
Json,
MessagePack,
Binary,
Cbor,
Protobuf,
}
pub trait Serializable {
fn supported_formats(&self) -> Vec<SerializationFormat>;
fn serialize(&self, format: SerializationFormat) -> CoreResult<Vec<u8>>;
fn deserialize(&mut self, data: &[u8], format: SerializationFormat) -> CoreResult<()>;
fn estimated_size(&self, format: SerializationFormat) -> usize;
}
pub trait CrossModuleOperator<Input, Output> {
fn apply(&self, input: &Input) -> CoreResult<Output>;
fn apply_inplace(&self, data: &mut Input) -> CoreResult<()>
where
Input: From<Output>;
fn operator_name(&self) -> &str;
fn input_info(&self) -> &str;
fn output_info(&self) -> &str;
fn is_deterministic(&self) -> bool;
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ApiVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
}
impl ApiVersion {
#[must_use]
pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
Self {
major,
minor,
patch,
}
}
#[must_use]
pub const fn is_compatible(&self, other: &Self) -> bool {
self.major == other.major && self.minor >= other.minor
}
#[must_use]
pub const fn is_newer_than(&self, other: &Self) -> bool {
if self.major != other.major {
return self.major > other.major;
}
if self.minor != other.minor {
return self.minor > other.minor;
}
self.patch > other.patch
}
}
impl fmt::Display for ApiVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
pub trait VersionedInterface {
fn version(&self) -> ApiVersion;
fn is_compatible_with(&self, version: &ApiVersion) -> bool {
self.version().is_compatible(version)
}
fn minimum_version(&self) -> ApiVersion;
fn deprecated_features(&self) -> Vec<String>;
}
pub trait ModuleInterface:
Identifiable + Configurable + ModuleCapability + VersionedInterface
{
fn initialize(&mut self) -> CoreResult<()>;
fn shutdown(&mut self) -> CoreResult<()>;
fn health_check(&self) -> CoreResult<HealthStatus>;
fn statistics(&self) -> HashMap<String, f64>;
fn reset(&mut self) -> CoreResult<()>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HealthStatus {
Healthy,
Degraded(String),
Unhealthy(String),
Unknown,
}
impl fmt::Display for HealthStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HealthStatus::Healthy => write!(f, "healthy"),
HealthStatus::Degraded(msg) => write!(f, "degraded: {msg}"),
HealthStatus::Unhealthy(msg) => write!(f, "unhealthy: {msg}"),
HealthStatus::Unknown => write!(f, "unknown"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ResourceUsage {
pub memory_bytes: usize,
pub cpu_percent: f32,
pub gpu_memory_bytes: usize,
pub thread_count: usize,
pub file_handles: usize,
pub custom: HashMap<String, f64>,
}
pub trait ResourceAware {
fn resource_usage(&self) -> ResourceUsage;
fn resource_limits(&self) -> ResourceUsage;
fn set_resource_limits(&mut self, limits: ResourceUsage) -> CoreResult<()>;
fn within_limits(&self) -> bool {
let usage = self.resource_usage();
let limits = self.resource_limits();
if limits.memory_bytes > 0 && usage.memory_bytes > limits.memory_bytes {
return false;
}
if limits.cpu_percent > 0.0 && usage.cpu_percent > limits.cpu_percent {
return false;
}
if limits.gpu_memory_bytes > 0 && usage.gpu_memory_bytes > limits.gpu_memory_bytes {
return false;
}
if limits.thread_count > 0 && usage.thread_count > limits.thread_count {
return false;
}
true
}
fn release_resources(&mut self) -> CoreResult<usize>;
fn estimate_resources(&self, operation: &str, input_size: usize) -> ResourceUsage;
}
#[derive(Debug, Clone)]
pub struct DiagnosticInfo {
pub component: String,
pub level: DiagnosticLevel,
pub message: String,
pub context: HashMap<String, String>,
pub timestamp: std::time::SystemTime,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DiagnosticLevel {
Trace,
Debug,
Info,
Warning,
Error,
}
impl fmt::Display for DiagnosticLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DiagnosticLevel::Trace => write!(f, "TRACE"),
DiagnosticLevel::Debug => write!(f, "DEBUG"),
DiagnosticLevel::Info => write!(f, "INFO"),
DiagnosticLevel::Warning => write!(f, "WARN"),
DiagnosticLevel::Error => write!(f, "ERROR"),
}
}
}
pub trait Diagnosable {
fn diagnostics(&self) -> Vec<DiagnosticInfo>;
fn diagnostics_at_level(&self, min_level: DiagnosticLevel) -> Vec<DiagnosticInfo> {
self.diagnostics()
.into_iter()
.filter(|d| d.level >= min_level)
.collect()
}
fn clear_diagnostics(&mut self);
fn enable_diagnostics(&mut self, enabled: bool);
fn diagnostics_enabled(&self) -> bool;
fn diagnostic_summary(&self) -> String {
let diags = self.diagnostics();
let errors = diags
.iter()
.filter(|d| d.level == DiagnosticLevel::Error)
.count();
let warnings = diags
.iter()
.filter(|d| d.level == DiagnosticLevel::Warning)
.count();
format!(
"{} diagnostics ({} errors, {} warnings)",
diags.len(),
errors,
warnings
)
}
}
pub trait Composable {
fn compose<Other: Composable>(&self, other: &Other) -> CoreResult<Box<dyn Any>>;
fn can_compose_with<Other: Composable>(&self, other: &Other) -> bool;
fn composition_requirements(&self) -> Vec<String>;
fn composition_outputs(&self) -> Vec<String>;
}
pub trait PipelineStage<Input, Output> {
fn process(&self, input: Input) -> CoreResult<Output>;
fn stage_name(&self) -> &str;
fn is_ready(&self) -> bool;
fn estimated_time_ms(&self, input_size: usize) -> f64;
}
pub trait Chainable<T> {
fn chain<F, U>(self, f: F) -> Chain<Self, F>
where
Self: Sized,
F: Fn(T) -> U;
fn map<F, U>(self, f: F) -> Map<Self, F>
where
Self: Sized,
F: Fn(T) -> U;
fn filter<F>(self, f: F) -> Filter<Self, F>
where
Self: Sized,
F: Fn(&T) -> bool;
}
#[derive(Debug)]
pub struct Chain<A, F> {
inner: A,
f: F,
}
impl<A, F> Chain<A, F> {
#[must_use]
pub const fn new(inner: A, f: F) -> Self {
Self { inner, f }
}
#[must_use]
pub const fn inner(&self) -> &A {
&self.inner
}
}
#[derive(Debug)]
pub struct Map<A, F> {
inner: A,
f: F,
}
impl<A, F> Map<A, F> {
#[must_use]
pub const fn new(inner: A, f: F) -> Self {
Self { inner, f }
}
}
#[derive(Debug)]
pub struct Filter<A, F> {
inner: A,
predicate: F,
}
impl<A, F> Filter<A, F> {
#[must_use]
pub const fn new(inner: A, predicate: F) -> Self {
Self { inner, predicate }
}
}
pub trait Observer<T> {
fn on_next(&mut self, value: T);
fn on_error(&mut self, error: CoreError);
fn on_complete(&mut self);
}
pub trait Observable<T> {
fn subscribe(&mut self, observer: Box<dyn Observer<T> + Send>);
fn unsubscribe(&mut self, observer_id: usize);
fn subscriber_count(&self) -> usize;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_version() {
let v1 = ApiVersion::new(1, 2, 3);
let v2 = ApiVersion::new(1, 3, 0);
let v3 = ApiVersion::new(2, 0, 0);
assert!(v2.is_compatible(&v1));
assert!(!v1.is_compatible(&v2));
assert!(!v3.is_compatible(&v1));
assert!(v2.is_newer_than(&v1));
assert!(v3.is_newer_than(&v2));
}
#[test]
fn test_version_display() {
let v = ApiVersion::new(1, 2, 3);
assert_eq!(v.to_string(), "1.2.3");
}
#[test]
fn test_capability_display() {
assert_eq!(Capability::Parallel.to_string(), "parallel");
assert_eq!(Capability::GpuAcceleration.to_string(), "gpu");
assert_eq!(Capability::Custom("test").to_string(), "custom:test");
}
#[test]
fn test_health_status() {
assert_eq!(HealthStatus::Healthy.to_string(), "healthy");
assert!(HealthStatus::Degraded("slow".into())
.to_string()
.contains("slow"));
assert!(HealthStatus::Unhealthy("failed".into())
.to_string()
.contains("failed"));
}
#[test]
fn test_resource_usage() {
let usage = ResourceUsage {
memory_bytes: 1024,
cpu_percent: 50.0,
thread_count: 4,
..Default::default()
};
assert_eq!(usage.memory_bytes, 1024);
assert_eq!(usage.cpu_percent, 50.0);
assert_eq!(usage.thread_count, 4);
}
#[test]
fn test_diagnostic_level_ordering() {
assert!(DiagnosticLevel::Error > DiagnosticLevel::Warning);
assert!(DiagnosticLevel::Warning > DiagnosticLevel::Info);
assert!(DiagnosticLevel::Info > DiagnosticLevel::Debug);
assert!(DiagnosticLevel::Debug > DiagnosticLevel::Trace);
}
#[test]
fn test_diagnostic_info() {
let info = DiagnosticInfo {
component: "test".into(),
level: DiagnosticLevel::Info,
message: "test message".into(),
context: HashMap::new(),
timestamp: std::time::SystemTime::now(),
};
assert_eq!(info.component, "test");
assert_eq!(info.level, DiagnosticLevel::Info);
}
#[test]
fn test_chain_combinator() {
let chain = Chain::new(42, |x: i32| x * 2);
assert_eq!(*chain.inner(), 42);
}
#[test]
fn test_serialization_format() {
let format = SerializationFormat::Json;
assert_eq!(format, SerializationFormat::Json);
}
}