helix_core/
error.rs

1use std::path::PathBuf;
2use std::fmt;
3use thiserror::Error;
4use crate::lexer::SourceLocation;
5
6#[derive(Error, Debug)]
7pub enum HlxError {
8    #[error("Configuration conversion failed: {field} - {details}")]
9    ConfigConversion {
10        field: String,
11        details: String,
12        suggestion: String
13    },
14
15    #[error("Dataset processing failed: {message}")]
16    DatasetProcessing {
17        message: String,
18        suggestion: String
19    },
20
21    #[error("Dataset quality validation failed: score {score:.2}")]
22    QualityValidation {
23        score: f64,
24        issues: Vec<String>,
25        suggestions: Vec<String>
26    },
27
28    #[error("Format conversion failed: {from} → {to}")]
29    FormatConversion {
30        from: String,
31        to: String,
32        suggestion: String
33    },
34
35    #[error("Algorithm '{algorithm}' not supported")]
36    UnsupportedAlgorithm {
37        algorithm: String,
38        supported: Vec<String>
39    },
40
41    #[error("Dataset not found: {path}")]
42    DatasetNotFound {
43        path: PathBuf,
44        suggestion: String
45    },
46
47    #[error("HLX processing failed: {message}")]
48    HlxProcessing {
49        message: String,
50        suggestion: String
51    },
52
53    #[error("Forge integration failed: {message}")]
54    ForgeIntegration {
55        message: String,
56        suggestion: String
57    },
58
59    #[error("Configuration validation failed: {field} = {value}")]
60    ConfigValidation {
61        field: String,
62        value: String,
63        suggestion: String
64    },
65}
66
67impl HlxError {
68    /// Create a configuration conversion error with suggestion
69    pub fn config_conversion(field: impl Into<String>, details: impl Into<String>) -> Self {
70        let field = field.into();
71        let details = details.into();
72        let suggestion = format!("Check your Forge.toml configuration for the '{}' field", field);
73        Self::ConfigConversion { field, details, suggestion }
74    }
75
76    /// Create a dataset processing error with suggestion
77    pub fn dataset_processing(message: impl Into<String>) -> Self {
78        let message = message.into();
79        let suggestion = "Try running 'forge hlx dataset validate' to check dataset compatibility".to_string();
80        Self::DatasetProcessing { message, suggestion }
81    }
82
83    /// Create a quality validation error
84    pub fn quality_validation(score: f64, issues: Vec<String>) -> Self {
85        let suggestions = vec![
86            "Run 'forge hlx dataset analyze' for detailed quality metrics".to_string(),
87            "Consider filtering or augmenting low-quality samples".to_string(),
88            "Check dataset format and required columns".to_string(),
89        ];
90        Self::QualityValidation { score, issues, suggestions }
91    }
92
93    /// Create a format conversion error
94    pub fn format_conversion(from: impl Into<String>, to: impl Into<String>) -> Self {
95        let from = from.into();
96        let to = to.into();
97        let suggestion = format!("Ensure your dataset contains the required fields for {} format", to);
98        Self::FormatConversion { from, to, suggestion }
99    }
100
101    /// Create an unsupported algorithm error
102    pub fn unsupported_algorithm(algorithm: impl Into<String>) -> Self {
103        let algorithm = algorithm.into();
104        let supported = vec!["bco", "dpo", "ppo", "sft"]
105            .into_iter()
106            .map(String::from)
107            .collect();
108        Self::UnsupportedAlgorithm { algorithm, supported }
109    }
110
111    /// Create a dataset not found error
112    pub fn dataset_not_found(path: PathBuf) -> Self {
113        let suggestion = format!("Ensure the dataset file exists at: {}", path.display());
114        Self::DatasetNotFound { path, suggestion }
115    }
116
117    /// Get recovery suggestions for this error
118    pub fn suggestions(&self) -> Vec<String> {
119        match self {
120            Self::ConfigConversion { suggestion, .. } => vec![suggestion.clone()],
121            Self::DatasetProcessing { suggestion, .. } => vec![suggestion.clone()],
122            Self::QualityValidation { suggestions, .. } => suggestions.clone(),
123            Self::FormatConversion { suggestion, .. } => vec![suggestion.clone()],
124            Self::UnsupportedAlgorithm { supported, .. } => {
125                vec![format!("Supported algorithms: {}", supported.join(", "))]
126            }
127            Self::DatasetNotFound { suggestion, .. } => vec![suggestion.clone()],
128            Self::HlxProcessing { suggestion, .. } => vec![suggestion.clone()],
129            Self::ForgeIntegration { suggestion, .. } => vec![suggestion.clone()],
130            Self::ConfigValidation { suggestion, .. } => vec![suggestion.clone()],
131        }
132    }
133
134    /// Check if this error is recoverable
135    pub fn is_recoverable(&self) -> bool {
136        match self {
137            Self::ConfigConversion { .. } => true,
138            Self::DatasetProcessing { .. } => true,
139            Self::QualityValidation { score, .. } => *score > 0.3, // Recoverable if quality isn't terrible
140            Self::FormatConversion { .. } => true,
141            Self::UnsupportedAlgorithm { .. } => false,
142            Self::DatasetNotFound { .. } => false,
143            Self::HlxProcessing { .. } => true,
144            Self::ForgeIntegration { .. } => true,
145            Self::ConfigValidation { .. } => true,
146        }
147    }
148}
149
150/// Result type for HLX operations
151pub type HlxResult<T> = std::result::Result<T, HlxError>;
152
153#[derive(Debug)]
154pub enum HelixError {
155    Lexer(LexerError),
156    Parser(ParserError),
157    Semantic(SemanticError),
158    Compilation(CompilationError),
159    Runtime(RuntimeError),
160    Io(IoError),
161}
162#[derive(Debug)]
163pub struct LexerError {
164    pub message: String,
165    pub location: SourceLocation,
166    pub source_line: String,
167    pub suggestion: Option<String>,
168}
169#[derive(Debug)]
170pub struct ParserError {
171    pub message: String,
172    pub location: SourceLocation,
173    pub expected: Vec<String>,
174    pub found: String,
175    pub source_line: String,
176    pub suggestion: Option<String>,
177}
178#[derive(Debug)]
179pub struct SemanticError {
180    pub kind: SemanticErrorKind,
181    pub location: SourceLocation,
182    pub entity: String,
183    pub context: Vec<String>,
184}
185#[derive(Debug)]
186pub enum SemanticErrorKind {
187    UndefinedReference,
188    DuplicateDefinition,
189    TypeMismatch { expected: String, found: String },
190    CircularDependency,
191    InvalidValue,
192    MissingRequired,
193    DeprecatedFeature,
194}
195#[derive(Debug)]
196pub struct CompilationError {
197    pub stage: CompilationStage,
198    pub message: String,
199    pub file: Option<PathBuf>,
200    pub recoverable: bool,
201}
202#[derive(Debug)]
203pub enum CompilationStage {
204    Parsing,
205    Validation,
206    Optimization,
207    CodeGeneration,
208    Serialization,
209    Bundling,
210}
211#[derive(Debug)]
212pub struct RuntimeError {
213    pub kind: RuntimeErrorKind,
214    pub message: String,
215    pub stack_trace: Vec<String>,
216}
217#[derive(Debug, PartialEq)]
218pub enum RuntimeErrorKind {
219    InvalidInstruction,
220    StackUnderflow,
221    StackOverflow,
222    MemoryAccessViolation,
223    DivisionByZero,
224    TypeConversion,
225    ResourceNotFound,
226}
227#[derive(Debug)]
228pub struct IoError {
229    pub operation: IoOperation,
230    pub path: PathBuf,
231    pub message: String,
232}
233#[derive(Debug)]
234pub enum IoOperation {
235    Read,
236    Write,
237    Create,
238    Delete,
239    Rename,
240    Metadata,
241}
242impl fmt::Display for HelixError {
243    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244        match self {
245            HelixError::Lexer(e) => write!(f, "{}", e),
246            HelixError::Parser(e) => write!(f, "{}", e),
247            HelixError::Semantic(e) => write!(f, "{}", e),
248            HelixError::Compilation(e) => write!(f, "{}", e),
249            HelixError::Runtime(e) => write!(f, "{}", e),
250            HelixError::Io(e) => write!(f, "{}", e),
251        }
252    }
253}
254impl fmt::Display for LexerError {
255    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256        writeln!(f, "Lexer error at {}:{}", self.location.line, self.location.column)?;
257        writeln!(f, "  {}", self.message)?;
258        writeln!(f, "  {}", self.source_line)?;
259        writeln!(f, "  {}^", " ".repeat(self.location.column))?;
260        if let Some(suggestion) = &self.suggestion {
261            writeln!(f, "  Suggestion: {}", suggestion)?;
262        }
263        Ok(())
264    }
265}
266impl fmt::Display for ParserError {
267    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268        writeln!(f, "Parser error at {}:{}", self.location.line, self.location.column)?;
269        writeln!(f, "  {}", self.message)?;
270        writeln!(f, "  {}", self.source_line)?;
271        writeln!(f, "  {}^", " ".repeat(self.location.column))?;
272        if !self.expected.is_empty() {
273            writeln!(f, "  Expected: {}", self.expected.join(" | "))?;
274        }
275        writeln!(f, "  Found: {}", self.found)?;
276        if let Some(suggestion) = &self.suggestion {
277            writeln!(f, "  Suggestion: {}", suggestion)?;
278        }
279        Ok(())
280    }
281}
282impl fmt::Display for SemanticError {
283    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284        write!(f, "Semantic error: ")?;
285        match &self.kind {
286            SemanticErrorKind::UndefinedReference => {
287                writeln!(f, "Undefined reference to '{}'", self.entity)?;
288            }
289            SemanticErrorKind::DuplicateDefinition => {
290                writeln!(f, "Duplicate definition of '{}'", self.entity)?;
291            }
292            SemanticErrorKind::TypeMismatch { expected, found } => {
293                writeln!(
294                    f, "Type mismatch for '{}': expected {}, found {}", self.entity,
295                    expected, found
296                )?;
297            }
298            SemanticErrorKind::CircularDependency => {
299                writeln!(f, "Circular dependency involving '{}'", self.entity)?;
300            }
301            SemanticErrorKind::InvalidValue => {
302                writeln!(f, "Invalid value for '{}'", self.entity)?;
303            }
304            SemanticErrorKind::MissingRequired => {
305                writeln!(f, "Missing required field '{}'", self.entity)?;
306            }
307            SemanticErrorKind::DeprecatedFeature => {
308                writeln!(f, "Use of deprecated feature '{}'", self.entity)?;
309            }
310        }
311        writeln!(f, "  at {}:{}", self.location.line, self.location.column)?;
312        if !self.context.is_empty() {
313            writeln!(f, "  Context:")?;
314            for ctx in &self.context {
315                writeln!(f, "    - {}", ctx)?;
316            }
317        }
318        Ok(())
319    }
320}
321impl fmt::Display for CompilationError {
322    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323        write!(f, "Compilation error during {:?}: {}", self.stage, self.message)?;
324        if let Some(file) = &self.file {
325            write!(f, " in file {:?}", file)?;
326        }
327        if self.recoverable {
328            write!(f, " (recoverable)")?;
329        }
330        Ok(())
331    }
332}
333impl fmt::Display for RuntimeError {
334    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
335        writeln!(f, "Runtime error: {:?}", self.kind)?;
336        writeln!(f, "  {}", self.message)?;
337        if !self.stack_trace.is_empty() {
338            writeln!(f, "  Stack trace:")?;
339            for frame in &self.stack_trace {
340                writeln!(f, "    {}", frame)?;
341            }
342        }
343        Ok(())
344    }
345}
346impl fmt::Display for IoError {
347    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348        write!(
349            f, "IO error during {:?} operation on {:?}: {}", self.operation, self.path,
350            self.message
351        )
352    }
353}
354impl std::error::Error for HelixError {}
355impl std::error::Error for LexerError {}
356impl std::error::Error for ParserError {}
357impl std::error::Error for SemanticError {}
358impl std::error::Error for CompilationError {}
359impl std::error::Error for RuntimeError {}
360impl std::error::Error for IoError {}
361impl From<std::io::Error> for HelixError {
362    fn from(err: std::io::Error) -> Self {
363        HelixError::Io(IoError {
364            operation: IoOperation::Read,
365            path: PathBuf::new(),
366            message: err.to_string(),
367        })
368    }
369}
370pub type Result<T> = std::result::Result<T, HelixError>;
371pub struct ErrorRecovery;
372impl ErrorRecovery {
373    pub fn suggest_for_undefined_reference(name: &str) -> Option<String> {
374        if name == "agnet" {
375            return Some("Did you mean 'agent'?".to_string());
376        }
377        if name == "worfklow" || name == "workfow" {
378            return Some("Did you mean 'workflow'?".to_string());
379        }
380        None
381    }
382    pub fn suggest_for_syntax_error(found: &str, expected: &[String]) -> Option<String> {
383        if expected.contains(&"=".to_string()) && found == ":" {
384            return Some("Use '=' for assignment, not ':'".to_string());
385        }
386        if expected.contains(&"{".to_string()) && found == "(" {
387            return Some("Use '{' for block start, not '('".to_string());
388        }
389        None
390    }
391}