jute/
errors.rs

1use std::{fmt, path::PathBuf};
2
3use std::error::Error;
4
5#[derive(Debug)]
6pub(crate) enum TypeValidationError {
7    UnknownType,
8    AmbigousType,
9}
10
11/// Top-level error type for the Jute compiler and code generator.
12/// `JuteError` represents all possible failures that can occur during:
13/// - Lexing
14/// - Parsing
15/// - Dependency resolution
16/// - Type validation
17/// - Code generation
18/// - I/O operations
19/// This enum is marked as `#[non_exhaustive]` to allow adding new error
20/// variants in the future without breaking downstream code.
21/// Consumers should match using a wildcard arm (`_`) to remain forward-compatible.
22#[derive(Debug)]
23#[non_exhaustive]
24pub enum JuteError {
25    /// An invalid conversion was attempted from a token to a primitive type.
26    /// This usually indicates a invalid token where permetive type was excepted
27    InvalidConversionToPremitive {
28        /// The token that failed conversion.
29        token: String,
30    },
31    /// An unexpected token was encountered during parsing.
32    /// This typically means the input does not conform to the Jute grammar.
33    UnexpectedToken {
34        /// The token that was encountered (if any).
35        token: Option<String>,
36        /// Human-readable explanation of what was expected.
37        message: String,
38    },
39    /// An unexpected character was encountered during lexing.
40    UnexpectedChar {
41        /// The unexpected character.
42        c: char,
43        /// Additional context about the error.
44        message: String,
45    },
46    // The token stream ended unexpectedly.
47    /// This usually indicates malformed or truncated input.
48    UnexpectedEndOfTokenStream,
49    /// The end of the source file was reached unexpectedly.
50    /// Common causes include missing braces or incomplete declarations.
51    UnexpectedEndOfFile,
52    /// Failed to canonicalize a filesystem path.
53    ///
54    /// This can occur when resolving include paths or source file locations.
55    PathCanonicalizeError {
56        /// Additional context explaining the failure.
57        message: String,
58    },
59    /// A multiline comment (`/* ... */`) was not properly terminated.
60    UnTerminatedMultiLineComment,
61    /// A quoted string literal was not properly terminated.
62    UnTerminatedQuotedString,
63    /// A quoted string literal contains a newline character,
64    QuotedStringWithNewLineCharacter,
65
66    /// A referenced type could not be resolved.
67    /// Provides detailed context to help diagnose schema issues.
68    UnknownType {
69        /// Field name
70        name: String,
71        /// Type
72        _type: String,
73        /// Parrent record/class
74        record: String,
75        /// Parrent module
76        module: String,
77        /// Src File
78        file: PathBuf,
79    },
80    // A referenced type name resolved to multiple definitions.
81    /// This usually happens when two included modules define
82    /// the same type name.
83    AmbiguousType {
84        /// The ambiguous type name.
85        name: String,
86        /// The expected kind of the type.
87        _type: String,
88        /// Record in which the ambiguity occurred.
89        record: String,
90        /// Module containing the record.
91        module: String,
92        /// Source file where the error occurred.
93        file: PathBuf,
94    },
95    /// A circular dependency was detected between modules.
96    /// The `cycle` field describes the dependency loop.
97    CircularDependency {
98        /// Human-readable representation of the dependency cycle.
99        cycle: String,
100    },
101    /// Wrapper around [`std::io::Error`].
102    /// Used for filesystem and I/O related failures.
103    Io(std::io::Error),
104    /// Wrapper around [`std::fmt::Error`].
105    ///
106    /// Typically occurs during code generation.
107    Fmt(std::fmt::Error),
108    /// Wrapper around UTF-8 decoding errors.
109    Utf8(std::string::FromUtf8Error),
110    /// Duplicate module names detected across source files.
111    DuplicateModuleName {
112        /// Name of the duplicated module.
113        module_name: String,
114        /// File in which the duplicate was found.
115        file_name: PathBuf,
116    },
117    /// Duplicate class names detected within the same module.
118    DuplicateClassName {
119        /// Name of the duplicated class.
120        class_name: String,
121        /// Module containing the duplicate.
122        module_name: String,
123        /// File in which the duplicate was found.
124        file_name: PathBuf,
125    },
126
127    /// Duplicate field names detected within a class.
128    DuplicateFieldName {
129        /// Name of the duplicated field.
130        field_name: String,
131        /// Class containing the duplicate field.
132        class_name: String,
133        /// Module containing the class.
134        module_name: String,
135        /// File in which the duplicate was found.
136        file_name: PathBuf,
137    },
138    /// An invalid field type was used in a class definition.
139    InvalidFieldType {
140        /// The invalid field type.
141        field_type: String,
142        /// Field name.
143        field_name: String,
144        /// Class containing the field.
145        class_name: String,
146        /// Module containing the class.
147        module_name: String,
148        /// File in which the error occurred.
149        file_name: PathBuf,
150    },
151}
152
153impl From<std::io::Error> for JuteError {
154    fn from(err: std::io::Error) -> Self {
155        JuteError::Io(err)
156    }
157}
158
159impl From<std::fmt::Error> for JuteError {
160    fn from(err: std::fmt::Error) -> Self {
161        JuteError::Fmt(err)
162    }
163}
164
165impl From<std::string::FromUtf8Error> for JuteError {
166    fn from(err: std::string::FromUtf8Error) -> Self {
167        JuteError::Utf8(err)
168    }
169}
170
171impl fmt::Display for JuteError {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        match self {
174            JuteError::UnexpectedChar { c, message } => {
175                write!(f, "unexpected character '{}': {}", c, message)
176            }
177
178            JuteError::UnexpectedToken { token, message } => {
179                write!(f, "unexpected token {:?}: {}", token, message)
180            }
181
182            JuteError::UnknownType {
183                name,
184                record,
185                module,
186                file,
187                ..
188            } => write!(
189                f,
190                "unknown type '{}' in record '{}' (module '{}') at {:?}",
191                name, record, module, file
192            ),
193
194            JuteError::AmbiguousType {
195                name,
196                record,
197                module,
198                file,
199                ..
200            } => write!(
201                f,
202                "ambiguous type '{}' in record '{}' (module '{}') at {:?}",
203                name, record, module, file
204            ),
205
206            JuteError::CircularDependency { cycle } => {
207                write!(f, "circular dependency detected: {}", cycle)
208            }
209
210            JuteError::Io(e) => write!(f, "io error: {}", e),
211            JuteError::Fmt(e) => write!(f, "formatting error: {}", e),
212
213            _ => write!(f, "{:?}", self),
214        }
215    }
216}
217
218impl Error for JuteError {
219    fn source(&self) -> Option<&(dyn Error + 'static)> {
220        match self {
221            JuteError::Io(e) => Some(e),
222            JuteError::Fmt(e) => Some(e),
223            JuteError::Utf8(e) => Some(e),
224            _ => None,
225        }
226    }
227}