Skip to main content

phago_core/
error.rs

1//! Error types for Phago operations.
2//!
3//! Provides structured error handling instead of panics.
4
5use std::fmt;
6use std::error::Error;
7
8/// Result type for Phago operations.
9pub type Result<T> = std::result::Result<T, PhagoError>;
10
11/// Errors that can occur during Phago operations.
12#[derive(Debug, Clone)]
13pub enum PhagoError {
14    /// Document-related errors.
15    Document(DocumentError),
16    /// Graph-related errors.
17    Graph(GraphError),
18    /// Agent-related errors.
19    Agent(AgentError),
20    /// Session-related errors.
21    Session(SessionError),
22    /// Query-related errors.
23    Query(QueryError),
24    /// Configuration errors.
25    Config(ConfigError),
26    /// I/O errors (wrapped).
27    Io(String),
28    /// Serialization errors.
29    Serialization(String),
30}
31
32impl fmt::Display for PhagoError {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            PhagoError::Document(e) => write!(f, "Document error: {}", e),
36            PhagoError::Graph(e) => write!(f, "Graph error: {}", e),
37            PhagoError::Agent(e) => write!(f, "Agent error: {}", e),
38            PhagoError::Session(e) => write!(f, "Session error: {}", e),
39            PhagoError::Query(e) => write!(f, "Query error: {}", e),
40            PhagoError::Config(e) => write!(f, "Config error: {}", e),
41            PhagoError::Io(msg) => write!(f, "I/O error: {}", msg),
42            PhagoError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
43        }
44    }
45}
46
47impl Error for PhagoError {}
48
49impl From<std::io::Error> for PhagoError {
50    fn from(e: std::io::Error) -> Self {
51        PhagoError::Io(e.to_string())
52    }
53}
54
55impl From<serde_json::Error> for PhagoError {
56    fn from(e: serde_json::Error) -> Self {
57        PhagoError::Serialization(e.to_string())
58    }
59}
60
61/// Document-related errors.
62#[derive(Debug, Clone)]
63pub enum DocumentError {
64    /// Document not found.
65    NotFound(String),
66    /// Document already digested.
67    AlreadyDigested(String),
68    /// Empty content.
69    EmptyContent,
70    /// Invalid format.
71    InvalidFormat(String),
72}
73
74impl fmt::Display for DocumentError {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        match self {
77            DocumentError::NotFound(id) => write!(f, "Document not found: {}", id),
78            DocumentError::AlreadyDigested(id) => write!(f, "Document already digested: {}", id),
79            DocumentError::EmptyContent => write!(f, "Document content is empty"),
80            DocumentError::InvalidFormat(msg) => write!(f, "Invalid document format: {}", msg),
81        }
82    }
83}
84
85/// Graph-related errors.
86#[derive(Debug, Clone)]
87pub enum GraphError {
88    /// Node not found.
89    NodeNotFound(String),
90    /// Edge not found.
91    EdgeNotFound(String, String),
92    /// Duplicate node.
93    DuplicateNode(String),
94    /// Invalid weight (must be 0.0-1.0).
95    InvalidWeight(f64),
96    /// Graph is empty.
97    EmptyGraph,
98}
99
100impl fmt::Display for GraphError {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match self {
103            GraphError::NodeNotFound(id) => write!(f, "Node not found: {}", id),
104            GraphError::EdgeNotFound(from, to) => write!(f, "Edge not found: {} -> {}", from, to),
105            GraphError::DuplicateNode(label) => write!(f, "Duplicate node: {}", label),
106            GraphError::InvalidWeight(w) => write!(f, "Invalid weight: {} (must be 0.0-1.0)", w),
107            GraphError::EmptyGraph => write!(f, "Graph is empty"),
108        }
109    }
110}
111
112/// Agent-related errors.
113#[derive(Debug, Clone)]
114pub enum AgentError {
115    /// Agent not found.
116    NotFound(String),
117    /// Agent already exists.
118    AlreadyExists(String),
119    /// Agent is busy.
120    Busy(String),
121    /// Agent is dead.
122    Dead(String),
123    /// Invalid action.
124    InvalidAction(String),
125}
126
127impl fmt::Display for AgentError {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        match self {
130            AgentError::NotFound(id) => write!(f, "Agent not found: {}", id),
131            AgentError::AlreadyExists(id) => write!(f, "Agent already exists: {}", id),
132            AgentError::Busy(id) => write!(f, "Agent is busy: {}", id),
133            AgentError::Dead(id) => write!(f, "Agent is dead: {}", id),
134            AgentError::InvalidAction(msg) => write!(f, "Invalid action: {}", msg),
135        }
136    }
137}
138
139/// Session-related errors.
140#[derive(Debug, Clone)]
141pub enum SessionError {
142    /// Session not found.
143    NotFound(String),
144    /// Session file corrupt.
145    Corrupt(String),
146    /// Version mismatch.
147    VersionMismatch { expected: String, found: String },
148    /// Save failed.
149    SaveFailed(String),
150    /// Load failed.
151    LoadFailed(String),
152}
153
154impl fmt::Display for SessionError {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match self {
157            SessionError::NotFound(path) => write!(f, "Session not found: {}", path),
158            SessionError::Corrupt(msg) => write!(f, "Session file corrupt: {}", msg),
159            SessionError::VersionMismatch { expected, found } => {
160                write!(f, "Version mismatch: expected {}, found {}", expected, found)
161            }
162            SessionError::SaveFailed(msg) => write!(f, "Save failed: {}", msg),
163            SessionError::LoadFailed(msg) => write!(f, "Load failed: {}", msg),
164        }
165    }
166}
167
168/// Query-related errors.
169#[derive(Debug, Clone)]
170pub enum QueryError {
171    /// Empty query.
172    EmptyQuery,
173    /// No results found.
174    NoResults,
175    /// Invalid parameters.
176    InvalidParameters(String),
177    /// Timeout.
178    Timeout,
179}
180
181impl fmt::Display for QueryError {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        match self {
184            QueryError::EmptyQuery => write!(f, "Query is empty"),
185            QueryError::NoResults => write!(f, "No results found"),
186            QueryError::InvalidParameters(msg) => write!(f, "Invalid parameters: {}", msg),
187            QueryError::Timeout => write!(f, "Query timed out"),
188        }
189    }
190}
191
192/// Configuration errors.
193#[derive(Debug, Clone)]
194pub enum ConfigError {
195    /// Invalid value.
196    InvalidValue { field: String, value: String, reason: String },
197    /// Missing required field.
198    MissingField(String),
199    /// Out of range.
200    OutOfRange { field: String, min: f64, max: f64, value: f64 },
201}
202
203impl fmt::Display for ConfigError {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        match self {
206            ConfigError::InvalidValue { field, value, reason } => {
207                write!(f, "Invalid value for {}: {} ({})", field, value, reason)
208            }
209            ConfigError::MissingField(field) => write!(f, "Missing required field: {}", field),
210            ConfigError::OutOfRange { field, min, max, value } => {
211                write!(f, "{} out of range: {} (must be {}-{})", field, value, min, max)
212            }
213        }
214    }
215}
216
217// Convenience constructors
218impl PhagoError {
219    pub fn document_not_found(id: impl Into<String>) -> Self {
220        PhagoError::Document(DocumentError::NotFound(id.into()))
221    }
222
223    pub fn node_not_found(id: impl Into<String>) -> Self {
224        PhagoError::Graph(GraphError::NodeNotFound(id.into()))
225    }
226
227    pub fn agent_not_found(id: impl Into<String>) -> Self {
228        PhagoError::Agent(AgentError::NotFound(id.into()))
229    }
230
231    pub fn empty_query() -> Self {
232        PhagoError::Query(QueryError::EmptyQuery)
233    }
234
235    pub fn invalid_config(field: impl Into<String>, value: impl Into<String>, reason: impl Into<String>) -> Self {
236        PhagoError::Config(ConfigError::InvalidValue {
237            field: field.into(),
238            value: value.into(),
239            reason: reason.into(),
240        })
241    }
242}