asdi/
error.rs

1/*!
2This module provides the common `Error` and `Result` types for this library.
3
4![module UML](https://raw.githubusercontent.com/johnstonskj/rust-asdi/main/book/src/model/error.svg)
5
6 */
7
8use crate::edb::PredicateRef;
9use crate::features::Feature;
10use std::fmt::{Display, Formatter};
11
12// ------------------------------------------------------------------------------------------------
13// Public Types
14// ------------------------------------------------------------------------------------------------
15
16///
17/// A line/column location within a source file, used for error reporting.
18///
19#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct SourceLocation {
21    line: usize,
22    column: usize,
23}
24
25///
26/// The type for all errors returned from this library. In the case of `ParserError` the
27/// implementation of display will use the underlying Pest error and so give a nicely
28/// formatted response.
29///
30#[derive(Debug)]
31pub enum Error {
32    /// A wrapper around an underlying [`std::io::Error`].
33    FileIoError(std::io::Error),
34
35    /// A wrapper around an underlying [`std::fmt::Error`].
36    FormatError(std::fmt::Error),
37
38    /// A wrapper around an underlying [`std::error::Error`] denoting a Pest parser error.
39    ParserError(Box<dyn std::error::Error>),
40
41    /// A wrapper around any underlying serialization error.
42    Serialization(Box<dyn std::error::Error>),
43
44    /// A serialization string in an `.input` or `.output` pragma is not a supported
45    /// serialization format.
46    SerializationFormatUnknown { format: String },
47
48    /// Either serialization, or deserialization, operation is not supported by the serialization
49    /// format.
50    SerializationOperationUnsupported { format: String },
51
52    /// An operation cannot be performed with this feature disabled.
53    LanguageFeatureDisabled { feature: Feature },
54
55    /// An operation cannot be performed with this feature enabled.
56    LanguageFeatureUnsupported { feature: Feature },
57
58    /// The value is not a valid representation for the expected type.
59    InvalidValue {
60        expecting_type: String,
61        given_value: String,
62    },
63
64    /// The provided fact values do not conform to the schema requirements for corresponding relation.
65    FactDoesNotConformToSchema { label: PredicateRef, terms: String },
66
67    /// A predicate from the EDB was present in a rule's head.
68    ExtensionalPredicateInRuleHead {
69        label: PredicateRef,
70        location: Option<SourceLocation>,
71    },
72
73    /// A rule has an invalid number of head atoms for current feature set.
74    InvalidHeadAtomCount {
75        actual: usize,
76        min: usize,
77        max: usize,
78        location: Option<SourceLocation>,
79    },
80
81    /// A rule has variable(s) in the head that do not appear in any positive literal in the body.
82    HeadVariablesMissingInBody {
83        atom: PredicateRef,
84        variables: Vec<String>,
85        location: Option<SourceLocation>,
86    },
87
88    /// A rule has variable(s) in some negative literal(s) that do not appear in any positive literal in the body.
89    NegativeVariablesNotAlsoPositive {
90        atom: PredicateRef,
91        variables: Vec<String>,
92        location: Option<SourceLocation>,
93    },
94
95    /// A rule has variable(s) in some arithmetic literal(s) that do not appear in any positive literal in the body.
96    ArithmeticVariablesNotAlsoPositive {
97        atom: PredicateRef,
98        variables: Vec<String>,
99        location: Option<SourceLocation>,
100    },
101
102    /// The named relation already exists in the extensional database.
103    RelationExists { label: PredicateRef },
104
105    /// The named relation does not exist in the selected database
106    RelationDoesNotExist { label: PredicateRef },
107
108    /// The named attribute was not a member of the relation or view schema.
109    AttributeDoesNotExist { label: String },
110
111    /// The attribute index is not valid for the relation or view schema.
112    AttributeIndexInvalid { index: usize },
113
114    /// The program cannot be evaluated as it includes negation but cannot be stratified.
115    NotStratifiable,
116
117    /// The arity of facts must be greater than, or equal to, 1.
118    NullaryFactsNotAllowed,
119
120    /// A comparison operator, or selection criteria, will always be true/⊤.
121    ComparisonIsAlwaysTrue { comparison: String },
122
123    /// A comparison operator, or selection criteria, will always be false/⊥.
124    ComparisonIsAlwaysFalse { comparison: String },
125
126    /// A requested operation cannot be performed as the values have incompatible types.
127    IncompatibleTypes { lhs_type: String, rhs_type: String },
128
129    /// Anonymous variables not allowed in the current context.
130    AnonymousVariableNotAllowed,
131}
132
133///
134/// The result of operations where the error returned is `asdi::error::Error`.
135///
136pub type Result<T> = std::result::Result<T, Error>;
137
138// ------------------------------------------------------------------------------------------------
139// Public Functions
140// ------------------------------------------------------------------------------------------------
141
142/// A serialization string in an `.input` or `.output` pragma is not a supported
143/// serialization format.
144#[inline]
145pub fn serialization_format_unknown<S: Into<String>>(format: S) -> Error {
146    Error::SerializationFormatUnknown {
147        format: format.into(),
148    }
149}
150
151/// Either serialization, or deserialization, operation is not supported by the serialization
152/// format.
153#[inline]
154pub fn serialization_operation_unsupported<S: Into<String>>(format: S) -> Error {
155    Error::SerializationOperationUnsupported {
156        format: format.into(),
157    }
158}
159
160/// An operation cannot be performed with this feature disabled.
161#[inline]
162pub fn language_feature_disabled(feature: Feature) -> Error {
163    Error::LanguageFeatureDisabled { feature }
164}
165
166/// An operation cannot be performed with this feature enabled.
167#[inline]
168pub fn language_feature_unsupported(feature: Feature) -> Error {
169    Error::LanguageFeatureUnsupported { feature }
170}
171
172/// The value is not a valid representation for the expected type.
173#[inline]
174pub fn invalid_value<S: Into<String>>(expecting_type: S, given_value: S) -> Error {
175    Error::InvalidValue {
176        expecting_type: expecting_type.into(),
177        given_value: given_value.into(),
178    }
179}
180
181/// The provided fact values do not conform to the schema requirements for corresponding relation.
182#[inline]
183pub fn fact_does_not_correspond_to_schema<S: Into<String>>(label: PredicateRef, terms: S) -> Error {
184    Error::FactDoesNotConformToSchema {
185        label,
186        terms: terms.into(),
187    }
188}
189
190/// A predicate from the EDB was present in a rule's head.
191#[inline]
192pub fn extensional_predicate_in_rule_head(
193    label: PredicateRef,
194    location: Option<SourceLocation>,
195) -> Error {
196    Error::ExtensionalPredicateInRuleHead { label, location }
197}
198
199/// A rule has an invalid number of head atoms for current feature set.
200#[inline]
201pub fn invalid_head_atom_count(
202    actual: usize,
203    min: usize,
204    max: usize,
205    location: Option<SourceLocation>,
206) -> Error {
207    Error::InvalidHeadAtomCount {
208        actual,
209        min,
210        max,
211        location,
212    }
213}
214
215/// A rule has variable(s) in the head that do not appear in any positive literal in the body.
216#[inline]
217pub fn head_variables_missing_in_body(
218    atom: PredicateRef,
219    variables: Vec<String>,
220    location: Option<SourceLocation>,
221) -> Error {
222    Error::HeadVariablesMissingInBody {
223        atom,
224        variables,
225        location,
226    }
227}
228
229/// A rule has variable(s) in some negative literal(s) that do not appear in any positive literal in the body.
230#[inline]
231pub fn negative_variables_not_also_positive(
232    atom: PredicateRef,
233    variables: Vec<String>,
234    location: Option<SourceLocation>,
235) -> Error {
236    Error::NegativeVariablesNotAlsoPositive {
237        atom,
238        variables,
239        location,
240    }
241}
242
243/// A rule has variable(s) in some arithmetic literal(s) that do not appear in any positive literal in the body.
244#[inline]
245pub fn arithmetic_variables_not_also_positive(
246    atom: PredicateRef,
247    variables: Vec<String>,
248    location: Option<SourceLocation>,
249) -> Error {
250    Error::ArithmeticVariablesNotAlsoPositive {
251        atom,
252        variables,
253        location,
254    }
255}
256
257/// The named relation already exists in the extensional database.
258#[inline]
259pub fn relation_exists(label: PredicateRef) -> Error {
260    Error::RelationExists { label }
261}
262
263#[inline]
264pub fn relation_does_not_exist(label: PredicateRef) -> Error {
265    Error::RelationDoesNotExist { label }
266}
267
268/// The attribute does not exist in the selected relation or view schema
269#[inline]
270pub fn attribute_does_not_exist<S: Into<String>>(label: S) -> Error {
271    Error::AttributeDoesNotExist {
272        label: label.into(),
273    }
274}
275
276/// The attribute index is not valid for the relation or view schema.
277#[inline]
278pub fn attribute_index_invalid(index: usize) -> Error {
279    Error::AttributeIndexInvalid { index }
280}
281
282/// The program cannot be evaluated as it includes negation but cannot be stratified.
283#[inline]
284pub fn program_not_stratifiable() -> Error {
285    Error::NotStratifiable
286}
287
288/// The arity of facts must be greater than, or equal to, 1.
289#[inline]
290pub fn nullary_facts_not_allowed() -> Error {
291    Error::NullaryFactsNotAllowed
292}
293
294/// A comparison operator, or selection criteria, will always be true/⊤.
295#[inline]
296pub fn comparison_is_always_true<S: Into<String>>(comparison: S) -> Error {
297    Error::ComparisonIsAlwaysTrue {
298        comparison: comparison.into(),
299    }
300}
301
302/// A comparison operator, or selection criteria, will always be false/⊥.
303#[inline]
304pub fn comparison_is_always_false<S: Into<String>>(comparison: S) -> Error {
305    Error::ComparisonIsAlwaysFalse {
306        comparison: comparison.into(),
307    }
308}
309
310/// Anonymous variables not allowed in the current context.
311#[inline]
312pub fn anonymous_variable_not_allowed() -> Error {
313    Error::AnonymousVariableNotAllowed
314}
315
316/// A requested operation cannot be performed as the values have incompatible types.
317#[inline]
318pub fn incompatible_types<S: Into<String>>(lhs_type: S, rhs_type: S) -> Error {
319    Error::IncompatibleTypes {
320        lhs_type: lhs_type.into(),
321        rhs_type: rhs_type.into(),
322    }
323}
324
325// ------------------------------------------------------------------------------------------------
326// Implementations
327// ------------------------------------------------------------------------------------------------
328
329impl From<(usize, usize)> for SourceLocation {
330    fn from(v: (usize, usize)) -> Self {
331        Self {
332            line: v.0,
333            column: v.1,
334        }
335    }
336}
337
338impl Display for SourceLocation {
339    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
340        write!(f, "[line {}, column {}]", self.line, self.column)
341    }
342}
343
344impl SourceLocation {
345    /// The line number (0-based) at which the error occurred.
346    pub fn line(&self) -> usize {
347        self.line
348    }
349
350    /// The column, or character, offset (0-based) at which the error occurred.
351    pub fn column(&self) -> usize {
352        self.column
353    }
354}
355
356// ------------------------------------------------------------------------------------------------
357
358impl Display for Error {
359    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
360        write!(
361            f,
362            "{}",
363            match self {
364                Self::FileIoError(e) => format!("File IO error, {}", e),
365                Self::FormatError(e) => format!("Formatting error, {}", e),
366                Self::ParserError(e) => e.to_string(),
367                Self::InvalidValue { expecting_type, given_value } =>
368                    format!("Invalid value for type {}, given {:?}", expecting_type, given_value),
369                Error::LanguageFeatureDisabled{ feature } =>
370                    format!("The language feature {} is not enabled.", feature.label()),
371                Error::LanguageFeatureUnsupported{ feature } =>
372                    format!("The language feature {} is not supported by the attempted operation.", feature.label()),
373                Error::ExtensionalPredicateInRuleHead { label, location } =>
374                    format!("A predicate {} from the EDB was present in a rule head{}.", label, match location {
375                        None => String::new(),
376                        Some(src) => format!(" (at {})", src),
377                    }),
378                Error::InvalidHeadAtomCount { actual, min, max, location } =>
379                    format!(
380                        "Rule{} has an invalid number of head atoms, {}, for current feature set. expecting {}..{}",
381                        match location {
382                            None => String::new(),
383                            Some(src) => format!(" (at {})", src),
384                        },
385                        actual,
386                        min,max,
387                    ),
388                Error::HeadVariablesMissingInBody { atom, variables, location } =>
389                    format!(
390                        "In rule '{}'{}, the variable(s) '{}' in the head do not appear in any positive literal in the body.",
391                        atom,
392                        match location {
393                            None => String::new(),
394                            Some(src) => format!(" (at {})", src),
395                        },
396                        variables.join(", ")
397                    ),
398                Error::NegativeVariablesNotAlsoPositive{ atom, variables, location } =>
399                    format!(
400                        "In rule '{}'{}, the variables '{}' in some negative literal(s) do not appear in any positive literal in the body.",
401                        atom,
402                        match location {
403                            None => String::new(),
404                            Some(src) => format!(", at {}", src),
405                        },
406                        variables.join(", ")
407                    ),
408                Error::ArithmeticVariablesNotAlsoPositive{ atom, variables, location } =>
409                    format!(
410                        "In rule '{}'{}, the variables '{}' in some arithmetic literal(s) do not appear in any positive literal in the body.",
411                        atom,
412                        match location {
413                            None => String::new(),
414                            Some(src) => format!(", at {}", src),
415                        },
416                        variables.join(", ")
417                    ),
418                Error::RelationExists { label } => format!("The relation '{}' already exists in the extensional database.", label),
419                Error::RelationDoesNotExist { label } => format!("The relation '{}' does not exist in the selected database.", label),
420                Error::FactDoesNotConformToSchema { label, terms } => format!("The fact values ({}) do not meet the schema requirements for relation '{}'", terms, label),
421                Error::Serialization(e) => format!("An error occured either serializing or deserializing a relation: {}", e),
422                Error::SerializationFormatUnknown { format: serialization } => format!("'{}' is not a supported serialization format.", serialization),
423                Error::SerializationOperationUnsupported{ format: serialization } => format!("The requested I/O operation is not supported by the serialization format '{}'", serialization),
424                Error::NotStratifiable => "The program cannot be evaluated as it includes negation but cannot be stratified.".to_string(),
425                Error::AttributeDoesNotExist { label } => format!("The attribute labeled '{}' was not a member of the relation or view schema.", label),
426                Error::AttributeIndexInvalid { index } => format!("The attribute index '{}' is not valid for the relation or view schema.", index),
427                Error::NullaryFactsNotAllowed => "The arity of facts must be greater than, or equal to, 1.".to_string(),
428                Error::ComparisonIsAlwaysTrue { comparison } => format!("A comparison operator, or selection criteria, will always be true/⊤ (`{}`).", comparison),
429                Error::ComparisonIsAlwaysFalse { comparison } => format!("A comparison operator, or selection criteria, will always be false/⊥ (`{}`).", comparison),
430                Error::AnonymousVariableNotAllowed => "Anonymous variables not allowed in the current context.".to_string(),
431                Error::IncompatibleTypes { lhs_type, rhs_type } => format!("A requested operation cannot be performed as the values have incompatible types `{}`, `{}`.", lhs_type, rhs_type),
432            }
433        )
434    }
435}
436
437impl std::error::Error for Error {
438    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
439        match *self {
440            Self::FileIoError(ref e) => Some(e),
441            _ => None,
442        }
443    }
444}
445
446impl From<std::io::Error> for Error {
447    fn from(e: std::io::Error) -> Self {
448        Self::FileIoError(e)
449    }
450}
451
452impl From<std::fmt::Error> for Error {
453    fn from(e: std::fmt::Error) -> Self {
454        Self::FormatError(e)
455    }
456}
457
458impl<T> From<Error> for Result<T> {
459    fn from(v: Error) -> Self {
460        Err(v)
461    }
462}
463
464impl Error {}