use std::sync::{Arc, OnceLock};
use crate::exception::ExceptionManager;
use crate::port::PortDriverBase;
use crate::trace::TraceManager;
#[derive(Clone)]
pub struct PortServices {
trace: Arc<TraceManager>,
exceptions: Arc<ExceptionManager>,
}
impl PortServices {
pub fn new() -> Self {
let trace = Arc::new(TraceManager::new());
let exceptions = Arc::new(ExceptionManager::new());
trace.set_exception_sink(exceptions.clone());
Self { trace, exceptions }
}
pub fn global() -> Self {
static GLOBAL: OnceLock<PortServices> = OnceLock::new();
GLOBAL.get_or_init(PortServices::new).clone()
}
pub fn trace(&self) -> &Arc<TraceManager> {
&self.trace
}
pub fn exceptions(&self) -> &Arc<ExceptionManager> {
&self.exceptions
}
pub(crate) fn bind(&self, base: &mut PortDriverBase) {
base.trace = Some(self.trace.clone());
base.bind_exception_sink(self.exceptions.clone());
}
}
impl Default for PortServices {
fn default() -> Self {
Self::global()
}
}
impl std::fmt::Debug for PortServices {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PortServices").finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_trace_announces_on_its_own_services_exception_list() {
let global = PortServices::global();
let _other = PortServices::new();
for services in [&global, &PortServices::new()] {
let sink = services
.trace()
.exception_manager()
.expect("the trace is wired to a sink");
assert!(Arc::ptr_eq(&sink, services.exceptions()));
}
}
}