use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum JobsError {
#[error("[NIKA-200] Daemon already running (pid: {pid})")]
DaemonAlreadyRunning { pid: u32 },
#[error("[NIKA-201] Failed to start daemon: {reason}")]
DaemonStartFailed { reason: String },
#[error("[NIKA-202] Failed to stop daemon: {reason}")]
DaemonStopFailed { reason: String },
#[error("[NIKA-203] Daemon is not running")]
DaemonNotRunning,
#[error("[NIKA-204] PID file error: {reason}")]
PidFileError { reason: String },
#[error("[NIKA-205] Internal channel closed")]
ChannelClosed,
#[error("[NIKA-210] Job not found: {name}")]
JobNotFound { name: String },
#[error("[NIKA-211] Invalid job definition '{name}': {reason}")]
InvalidJobDefinition { name: String, reason: String },
#[error("[NIKA-212] Duplicate job name: {name}")]
DuplicateJobName { name: String },
#[error("[NIKA-213] Workflow not found: {path:?}")]
WorkflowNotFound { path: PathBuf },
#[error("[NIKA-214] Job '{name}' is already running")]
JobAlreadyRunning { name: String },
#[error("[NIKA-220] Invalid cron expression: {expression}")]
InvalidCronExpression { expression: String },
#[error("[NIKA-221] Webhook server error: {reason}")]
WebhookServerError { reason: String },
#[error("[NIKA-222] Watch path error: {path:?} - {reason}")]
WatchPathError { path: PathBuf, reason: String },
#[error("[NIKA-223] Trigger configuration error: {reason}")]
TriggerConfigError { reason: String },
#[error("[NIKA-230] Job '{name}' execution failed: {reason}")]
ExecutionFailed { name: String, reason: String },
#[error("[NIKA-231] Job '{name}' timed out after {timeout_secs}s")]
ExecutionTimeout { name: String, timeout_secs: u64 },
#[error("[NIKA-232] Cannot cancel running execution: {execution_id}")]
CannotCancelRunning { execution_id: String },
#[error("[NIKA-233] Execution not found: {execution_id}")]
ExecutionNotFound { execution_id: String },
#[error("[NIKA-234] Job '{name}' exceeded max retries ({max_attempts})")]
MaxRetriesExceeded { name: String, max_attempts: u32 },
#[error("[NIKA-240] Database error: {reason}")]
DatabaseError { reason: String },
#[error("[NIKA-241] Failed to open database: {path:?}")]
DatabaseOpenFailed { path: PathBuf },
#[error("[NIKA-242] State serialization error: {reason}")]
SerializationError { reason: String },
#[error("[NIKA-243] Database migration error: {reason}")]
MigrationError { reason: String },
#[error("[NIKA-250] Notification failed: {channel} - {reason}")]
NotificationFailed { channel: String, reason: String },
#[error("[NIKA-251] Invalid notification config: {reason}")]
InvalidNotificationConfig { reason: String },
#[error("[NIKA-260] Configuration parse error: {reason}")]
ConfigParseError { reason: String },
#[error("[NIKA-261] Configuration file not found: {path:?}")]
ConfigNotFound { path: PathBuf },
#[error("[NIKA-262] Invalid configuration: {reason}")]
InvalidConfig { reason: String },
#[error("[NIKA-270] I/O error: {reason}")]
IoError { reason: String },
#[error("[NIKA-271] File system error: {path:?} - {reason}")]
FileSystemError { path: PathBuf, reason: String },
}
impl From<std::io::Error> for JobsError {
fn from(err: std::io::Error) -> Self {
JobsError::IoError {
reason: err.to_string(),
}
}
}
#[cfg(feature = "jobs")]
impl From<rusqlite::Error> for JobsError {
fn from(err: rusqlite::Error) -> Self {
JobsError::DatabaseError {
reason: err.to_string(),
}
}
}
impl From<serde_json::Error> for JobsError {
fn from(err: serde_json::Error) -> Self {
JobsError::SerializationError {
reason: err.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_codes() {
let err = JobsError::DaemonAlreadyRunning { pid: 1234 };
let msg = err.to_string();
assert!(msg.contains("NIKA-200"));
assert!(msg.contains("1234"));
}
#[test]
fn test_job_not_found() {
let err = JobsError::JobNotFound {
name: "test-job".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("NIKA-210"));
assert!(msg.contains("test-job"));
}
#[test]
fn test_execution_failed() {
let err = JobsError::ExecutionFailed {
name: "my-job".to_string(),
reason: "workflow error".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("NIKA-230"));
assert!(msg.contains("my-job"));
assert!(msg.contains("workflow error"));
}
#[test]
fn test_channel_closed() {
let err = JobsError::ChannelClosed;
let msg = err.to_string();
assert!(msg.contains("NIKA-205"));
}
#[test]
fn test_job_already_running() {
let err = JobsError::JobAlreadyRunning {
name: "exclusive-job".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("NIKA-214"));
assert!(msg.contains("exclusive-job"));
}
}