fastobo/
error.rs

1//! `Error` and `Result` types for this crate.
2
3use std::io::Error as IOError;
4
5use thiserror::Error;
6
7use crate::ast::*;
8use crate::syntax::pest::error::Error as PestError;
9use crate::syntax::pest::Span;
10use crate::syntax::Rule;
11
12/// An error for cardinality violation.
13///
14/// This error is highly dependent on the function that returns it: the `name`
15/// field can provide more information about the specific clause that errored
16/// to the end-user.
17#[derive(Clone, Debug, Eq, Error, PartialEq)]
18pub enum CardinalityError {
19    #[error("missing {name} clause")]
20    MissingClause { name: String },
21    #[error("duplicate {name} clauses")]
22    DuplicateClauses { name: String },
23    #[error("invalid single {name} clause")]
24    SingleClause { name: String },
25}
26
27impl CardinalityError {
28    pub fn missing<S: Into<String>>(name: S) -> Self {
29        CardinalityError::MissingClause { name: name.into() }
30    }
31
32    pub fn duplicate<S: Into<String>>(name: S) -> Self {
33        CardinalityError::DuplicateClauses { name: name.into() }
34    }
35
36    pub fn single<S: Into<String>>(name: S) -> Self {
37        CardinalityError::SingleClause { name: name.into() }
38    }
39}
40
41/// A syntax error.
42#[derive(Debug, Eq, Error, PartialEq)]
43pub enum SyntaxError {
44    /// An unexpected rule was used in `FromPair::from_pair`.
45    ///
46    /// # Example
47    /// ```rust
48    /// # extern crate fastobo;
49    /// # use fastobo::ast::*;
50    /// # use fastobo::parser::*;
51    /// # use fastobo::syntax::*;
52    /// let cache = Cache::new();
53    /// let pairs = Lexer::tokenize(Rule::UnquotedString, "hello, world!");
54    /// let pair = pairs.unwrap().next().unwrap();
55    /// # let err =
56    /// QuotedString::from_pair(pair, &cache).unwrap_err();
57    /// # match err {
58    /// #   fastobo::error::SyntaxError::UnexpectedRule { expected, actual } => {
59    /// #       assert_eq!(expected, Rule::QuotedString);
60    /// #       assert_eq!(actual, Rule::UnquotedString);
61    /// #   }
62    /// #   e => panic!("unexpected error: {:?}", e),
63    /// # };
64    /// ```
65    #[error("unexpected rule: {actual:?} (expected {expected:?})")]
66    UnexpectedRule { expected: Rule, actual: Rule },
67
68    /// The underlying parser encountered an error.
69    ///
70    /// # Example
71    /// ```rust
72    /// # extern crate fastobo;
73    /// # use std::str::FromStr;
74    /// # use fastobo::ast::*;
75    /// # let err =
76    /// QuotedString::from_str("definitely not a quoted string").unwrap_err();
77    /// # match err {
78    /// #   fastobo::error::SyntaxError::ParserError { .. } => (),
79    /// #   e => panic!("unexpected error: {:?}", e),
80    /// # };
81    /// ```
82    #[error("parser error: {error}")]
83    ParserError {
84        #[from]
85        error: Box<PestError<Rule>>,
86    },
87}
88
89impl SyntaxError {
90    /// Update the line of the error, if needed.
91    pub(crate) fn with_offsets(self, line_offset: usize, offset: usize) -> Self {
92        use self::SyntaxError::*;
93        use crate::syntax::pest::error::InputLocation;
94        use crate::syntax::pest::error::LineColLocation;
95        match self {
96            e @ UnexpectedRule { .. } => e,
97            ParserError { mut error } => {
98                error.location = match error.location {
99                    InputLocation::Pos(s) => InputLocation::Pos(s + offset),
100                    InputLocation::Span((s, e)) => InputLocation::Span((s + offset, e + offset)),
101                };
102                error.line_col = match error.line_col {
103                    LineColLocation::Pos((l, c)) => LineColLocation::Pos((l + line_offset, c)),
104                    LineColLocation::Span((ls, cs), (le, ce)) => {
105                        LineColLocation::Span((ls + line_offset, cs), (le + line_offset, ce))
106                    }
107                };
108                ParserError { error }
109            }
110        }
111    }
112
113    /// Update the path of the error, if needed.
114    pub(crate) fn with_path(self, path: &str) -> Self {
115        use self::SyntaxError::*;
116        match self {
117            e @ UnexpectedRule { .. } => e,
118            ParserError { error } => ParserError {
119                error: Box::new(error.with_path(path)),
120            },
121        }
122    }
123
124    /// Update the span of the error, if needed.
125    pub(crate) fn with_span(self, span: Span) -> Self {
126        use self::SyntaxError::*;
127        match self {
128            e @ UnexpectedRule { .. } => e,
129            ParserError { error } => {
130                // FIXME(@althonos): the new error should be spanned only if
131                //                   the original error is spanned, but there
132                //                   is no clean way to create an error at
133                //                   the right position with `pest::error`.
134                ParserError {
135                    error: Box::new(PestError::new_from_span(error.variant, span)),
136                }
137            }
138        }
139    }
140}
141
142impl From<PestError<Rule>> for SyntaxError {
143    fn from(error: PestError<Rule>) -> Self {
144        Self::from(Box::new(error))
145    }
146}
147
148/// A threading error.
149#[cfg(feature = "threading")]
150#[cfg_attr(feature = "_doc", doc(cfg(feature = "threading")))]
151#[derive(Debug, Eq, Error, PartialEq)]
152pub enum ThreadingError {
153    /// A communication channel unexpectedly disconnected.
154    #[error("disconnected channel")]
155    DisconnectedChannel,
156}
157
158/// The result type for this crate.
159pub type Result<T> = std::result::Result<T, Error>;
160
161/// The error type for this crate.
162#[derive(Debug, Error)]
163pub enum Error {
164    /// A syntax error occurred.
165    #[error("syntax error: {error}")]
166    SyntaxError {
167        #[from]
168        error: SyntaxError,
169    },
170
171    /// An IO error occurred.
172    ///
173    /// # Example
174    /// ```rust
175    /// # extern crate fastobo;
176    /// # use fastobo::ast::*;
177    /// # let err =
178    /// fastobo::from_file("some/non-existing/path").unwrap_err();
179    /// # match err {
180    /// #   fastobo::error::Error::IOError { .. } => (),
181    /// #   e => panic!("unexpected error: {:?}", e),
182    /// # };
183    #[error("IO error: {error}")]
184    IOError {
185        #[from]
186        error: IOError,
187    },
188
189    /// A cardinality-related error occurred.
190    #[error("cardinality error: {inner}")]
191    CardinalityError {
192        id: Option<Ident>,
193        #[source]
194        inner: CardinalityError,
195    },
196
197    /// A threading-related error occurred.
198    #[cfg(feature = "threading")]
199    #[cfg_attr(feature = "_doc", doc(cfg(feature = "threading")))]
200    #[error("threading error: {error}")]
201    ThreadingError {
202        #[from]
203        error: ThreadingError,
204    },
205}