//! Domain errors for K-Notes //! //! Uses `thiserror` for ergonomic error definitions. //! These errors represent domain-level failures and will be mapped //! to HTTP status codes in the API layer. use thiserror::Error; use uuid::Uuid; /// Domain-level errors for K-TV operations #[derive(Debug, Error)] #[non_exhaustive] pub enum DomainError { /// The requested user was not found #[error("User not found: {0}")] UserNotFound(Uuid), /// User with this email/subject already exists #[error("User already exists: {0}")] UserAlreadyExists(String), /// The requested channel was not found #[error("Channel not found: {0}")] ChannelNotFound(Uuid), /// No generated schedule exists and is active for the given channel and time #[error("No active schedule for channel: {0}")] NoActiveSchedule(Uuid), /// A validation error occurred #[error("Validation error: {0}")] ValidationError(String), /// A timezone string could not be parsed #[error("Invalid timezone: {0}")] TimezoneError(String), /// User is not authenticated (maps to HTTP 401) #[error("Unauthenticated: {0}")] Unauthenticated(String), /// User is not allowed to perform this action (maps to HTTP 403) #[error("Forbidden: {0}")] Forbidden(String), /// A repository/infrastructure error occurred #[error("Repository error: {0}")] RepositoryError(String), /// An infrastructure adapter error occurred #[error("Infrastructure error: {0}")] InfrastructureError(String), } impl DomainError { /// Create a validation error pub fn validation(message: impl Into) -> Self { Self::ValidationError(message.into()) } /// Create an unauthenticated error (not logged in → 401) pub fn unauthenticated(message: impl Into) -> Self { Self::Unauthenticated(message.into()) } /// Create a forbidden error (not allowed → 403) pub fn forbidden(message: impl Into) -> Self { Self::Forbidden(message.into()) } /// Check if this error indicates a "not found" condition pub fn is_not_found(&self) -> bool { matches!( self, DomainError::UserNotFound(_) | DomainError::ChannelNotFound(_) ) } /// Check if this error indicates a conflict (already exists) pub fn is_conflict(&self) -> bool { matches!(self, DomainError::UserAlreadyExists(_)) } } impl From for DomainError { fn from(error: crate::value_objects::ValidationError) -> Self { DomainError::ValidationError(error.to_string()) } } /// Result type alias for domain operations pub type DomainResult = Result;