use crate::error::NikaError;
#[derive(Debug, thiserror::Error)]
pub enum ProviderError {
#[error("[NIKA-030] Provider '{provider}' not configured")]
NotConfigured { provider: String },
#[error("[NIKA-031] Provider API error: {message}")]
ApiError { message: String },
#[error("[NIKA-032] Missing API key for provider '{provider}'")]
MissingApiKey { provider: String },
#[error("[NIKA-033] Invalid configuration: {message}")]
InvalidConfig { message: String },
}
impl From<ProviderError> for NikaError {
fn from(e: ProviderError) -> Self {
match e {
ProviderError::NotConfigured { provider } => {
NikaError::ProviderNotConfigured { provider }
}
ProviderError::ApiError { message } => NikaError::ProviderApiError { message },
ProviderError::MissingApiKey { provider } => {
NikaError::MissingApiKey { provider }
}
ProviderError::InvalidConfig { message } => NikaError::InvalidConfig { message },
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum DagError {
#[error("[NIKA-020] Cycle detected in DAG: {cycle}")]
CycleDetected { cycle: String },
#[error("[NIKA-021] Missing dependency: task '{task_id}' depends on unknown '{dep_id}'")]
MissingDependency { task_id: String, dep_id: String },
#[error("[NIKA-022] Duplicate task ID: '{task_id}' appears multiple times")]
DuplicateTaskId { task_id: String },
}
impl From<DagError> for NikaError {
fn from(e: DagError) -> Self {
match e {
DagError::CycleDetected { cycle } => NikaError::CycleDetected { cycle },
DagError::MissingDependency { task_id, dep_id } => {
NikaError::MissingDependency { task_id, dep_id }
}
DagError::DuplicateTaskId { task_id } => NikaError::DuplicateTaskId { task_id },
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ExecutionError {
#[error("[NIKA-044] Exec error: {reason}")]
ExecFailed { reason: String },
#[error("[NIKA-045] Fetch error: {reason}")]
FetchFailed { reason: String },
#[error("[NIKA-046] Extract error: {reason}")]
ExtractFailed { reason: String },
#[error("[NIKA-096] Execution error: {0}")]
General(String),
#[error("[NIKA-097] Workflow cancelled: {phase}")]
Cancelled { phase: String },
#[error("[NIKA-098] Task panicked: {reason}")]
Panicked { reason: String },
}
impl From<ExecutionError> for NikaError {
fn from(e: ExecutionError) -> Self {
match e {
ExecutionError::ExecFailed { reason } => NikaError::ExecError { reason },
ExecutionError::FetchFailed { reason } => NikaError::FetchError { reason },
ExecutionError::ExtractFailed { reason } => NikaError::ExtractError { reason },
ExecutionError::General(msg) => NikaError::Execution(msg),
ExecutionError::Cancelled { phase } => NikaError::WorkflowCancelled { phase },
ExecutionError::Panicked { reason } => NikaError::TaskPanicked { reason },
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum BindingError {
#[error("[NIKA-041] Template error in '{template}': {reason}")]
TemplateError { template: String, reason: String },
#[error("[NIKA-042] Binding '{alias}' not found")]
NotFound { alias: String },
#[error("[NIKA-043] Binding type mismatch at '{path}': expected {expected}, got {actual}")]
TypeMismatch {
path: String,
expected: String,
actual: String,
},
}
impl From<BindingError> for NikaError {
fn from(e: BindingError) -> Self {
match e {
BindingError::TemplateError { template, reason } => {
NikaError::TemplateError { template, reason }
}
BindingError::NotFound { alias } => NikaError::BindingNotFound { alias },
BindingError::TypeMismatch {
path,
expected,
actual,
} => NikaError::BindingTypeMismatch {
path,
expected,
actual,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn provider_error_converts_to_nika_error() {
let err: NikaError = ProviderError::NotConfigured {
provider: "anthropic".into(),
}
.into();
assert!(err.to_string().contains("NIKA-030"));
assert!(err.to_string().contains("anthropic"));
}
#[test]
fn dag_error_converts_to_nika_error() {
let err: NikaError = DagError::CycleDetected {
cycle: "a → b → a".into(),
}
.into();
assert!(err.to_string().contains("NIKA-020"));
}
#[test]
fn execution_error_converts_to_nika_error() {
let err: NikaError = ExecutionError::General("test error".into()).into();
assert!(err.to_string().contains("NIKA-096"));
}
#[test]
fn binding_error_converts_to_nika_error() {
let err: NikaError = BindingError::NotFound {
alias: "data".into(),
}
.into();
assert!(err.to_string().contains("NIKA-042"));
}
#[test]
fn domain_errors_are_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ProviderError>();
assert_send_sync::<DagError>();
assert_send_sync::<ExecutionError>();
assert_send_sync::<BindingError>();
}
}