use std::sync::Arc;
#[derive(Debug, thiserror::Error)]
pub enum CordisError {
#[error("service {0:?} not found in scope")]
ServiceNotFound(String),
#[error("plugin failed")]
PluginFailed(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("multiple errors: {errors:?}")]
Aggregate {
errors: Vec<Arc<CordisError>>,
},
#[error("fiber disposed")]
InactiveEffect,
#[error("config validation failed: {issues:?}")]
Validation { issues: Vec<String> },
#[error("dependency unsatisfied: {0:?}")]
InjectUnsatisfied(Vec<String>),
#[error("service {0:?} already registered in scope")]
ServiceExists(String),
}
pub type ErrorSink = Arc<dyn Fn(Arc<CordisError>) + Send + Sync>;
pub(crate) fn default_sink() -> ErrorSink {
Arc::new(|e: Arc<CordisError>| eprintln!("[rutis] {e}"))
}
pub(crate) fn aggregate_errors(errors: Vec<CordisError>) -> Option<CordisError> {
match errors.len() {
0 => None,
1 => errors.into_iter().next(),
_ => Some(CordisError::Aggregate {
errors: errors.into_iter().map(Arc::new).collect(),
}),
}
}
pub(crate) fn aggregate_arcs(errors: Vec<Arc<CordisError>>) -> Option<Arc<CordisError>> {
match errors.len() {
0 => None,
1 => errors.into_iter().next(),
_ => Some(Arc::new(CordisError::Aggregate { errors })),
}
}
pub(crate) fn panic_error(p: Box<dyn std::any::Any + Send>) -> CordisError {
let msg = if let Some(s) = p.downcast_ref::<&str>() {
(*s).to_string()
} else if let Some(s) = p.downcast_ref::<String>() {
s.clone()
} else {
"panic in task".to_string()
};
CordisError::PluginFailed(msg.into())
}
pub(crate) fn join_panic_error(join_err: tokio::task::JoinError) -> CordisError {
if join_err.is_panic() {
panic_error(join_err.into_panic())
} else {
CordisError::PluginFailed("task cancelled before completion".into())
}
}