Skip to main content

libconfig/
error.rs

1//! Error types for libconfig serialization and deserialization
2
3use std::fmt::{self, Display};
4
5use serde::{de, ser};
6
7/// A Result type alias where the error is `libconfig::Error`
8pub type Result<T> = std::result::Result<T, Error>;
9
10/// An error that occurred while serializing or deserializing libconfig data.
11///
12/// Errors produced while parsing text carry the line and column (1-based) at
13/// which the problem was found; inspect them with [`Error::line`] and
14/// [`Error::column`]. The underlying cause is available as [`Error::kind`].
15#[derive(Debug)]
16pub struct Error {
17    kind: ErrorKind,
18    line: Option<usize>,
19    column: Option<usize>,
20}
21
22impl Error {
23    /// The kind of error that occurred.
24    pub fn kind(&self) -> &ErrorKind {
25        &self.kind
26    }
27
28    /// The 1-based line number where a parse error occurred, if known.
29    pub fn line(&self) -> Option<usize> {
30        self.line
31    }
32
33    /// The 1-based column number where a parse error occurred, if known.
34    pub fn column(&self) -> Option<usize> {
35        self.column
36    }
37
38    /// Attaches a source location, unless one is already present.
39    pub(crate) fn with_location(mut self, line: usize, column: usize) -> Self {
40        if self.line.is_none() {
41            self.line = Some(line);
42            self.column = Some(column);
43        }
44        self
45    }
46}
47
48impl From<ErrorKind> for Error {
49    fn from(kind: ErrorKind) -> Self {
50        Error {
51            kind,
52            line: None,
53            column: None,
54        }
55    }
56}
57
58/// The specific kind of error underlying an [`Error`].
59#[non_exhaustive]
60#[derive(Debug, Clone, PartialEq)]
61pub enum ErrorKind {
62    /// A custom error message from Serde
63    Message(String),
64
65    /// An I/O error, carrying the underlying error's message
66    Io(String),
67
68    /// Unexpected end of input
69    Eof,
70
71    /// Syntax error in the input
72    Syntax,
73
74    /// Expected a boolean value
75    ExpectedBoolean,
76
77    /// Expected an integer value
78    ExpectedInteger,
79
80    /// Expected a float value
81    ExpectedFloat,
82
83    /// Expected a string value
84    ExpectedString,
85
86    /// Expected an array
87    ExpectedArray,
88
89    /// Expected a comma in an array
90    ExpectedArrayComma,
91
92    /// Expected end of array
93    ExpectedArrayEnd,
94
95    /// Expected a semicolon after array
96    ExpectedArraySemicolon,
97
98    /// Expected a list
99    ExpectedList,
100
101    /// Expected a comma in a list
102    ExpectedListComma,
103
104    /// Expected end of list
105    ExpectedListEnd,
106
107    /// Expected a group (struct)
108    ExpectedGroup,
109
110    /// Expected an equals sign or colon
111    ExpectedEquals,
112
113    /// Expected a semicolon
114    ExpectedSemicolon,
115
116    /// Expected a comma or semicolon
117    ExpectedCommaOrSemicolon,
118
119    /// Expected end of group
120    ExpectedGroupEnd,
121
122    /// Expected an identifier
123    ExpectedIdentifier,
124
125    /// Trailing characters after parsing
126    TrailingCharacters,
127
128    /// Heterogeneous arrays are not supported
129    HeterogeneousArray,
130
131    /// Invalid escape sequence
132    InvalidEscape,
133
134    /// Invalid number format
135    InvalidNumber,
136
137    /// Maps with non-string keys are not supported
138    KeyMustBeAString,
139
140    /// Unit type not supported at top level
141    UnitNotSupported,
142
143    /// Type not supported
144    TypeNotSupported(String),
145
146    /// Integer value does not fit in the target type
147    IntegerOutOfRange,
148
149    /// Float value does not fit in the target type
150    FloatOutOfRange,
151
152    /// Non-finite floats (inf, NaN) cannot be represented in libconfig
153    NonFiniteFloat,
154
155    /// Setting name does not match `[A-Za-z*][-A-Za-z0-9_*]*`
156    InvalidSettingName(String),
157
158    /// Path passed to `lookup`/`set`/`remove` is malformed
159    InvalidPath(String),
160
161    /// A path traversal step failed (wrong container type or missing index)
162    PathTraversal(String),
163
164    /// Arrays may only contain scalar values
165    ArrayElementNotScalar,
166
167    /// Braces around the top level of a configuration are not allowed
168    /// (grammar: a configuration is a setting list; braces delimit nested
169    /// groups only)
170    TopLevelBraces,
171
172    /// A `/*` block comment was never closed with `*/` (section 2.10
173    /// defines block comments as delimited pairs)
174    UnterminatedComment,
175
176    /// Error processing an `@include` directive
177    Include(String),
178}
179
180impl ser::Error for Error {
181    fn custom<T: Display>(msg: T) -> Self {
182        ErrorKind::Message(msg.to_string()).into()
183    }
184}
185
186impl de::Error for Error {
187    fn custom<T: Display>(msg: T) -> Self {
188        ErrorKind::Message(msg.to_string()).into()
189    }
190}
191
192impl Display for ErrorKind {
193    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
194        match self {
195            ErrorKind::Message(msg) => formatter.write_str(msg),
196            ErrorKind::Io(msg) => write!(formatter, "i/o error: {}", msg),
197            ErrorKind::Eof => formatter.write_str("unexpected end of input"),
198            ErrorKind::Syntax => formatter.write_str("syntax error"),
199            ErrorKind::ExpectedBoolean => formatter.write_str("expected boolean"),
200            ErrorKind::ExpectedInteger => formatter.write_str("expected integer"),
201            ErrorKind::ExpectedFloat => formatter.write_str("expected float"),
202            ErrorKind::ExpectedString => formatter.write_str("expected string"),
203            ErrorKind::ExpectedArray => formatter.write_str("expected array"),
204            ErrorKind::ExpectedArrayComma => formatter.write_str("expected comma in array"),
205            ErrorKind::ExpectedArrayEnd => formatter.write_str("expected end of array"),
206            ErrorKind::ExpectedArraySemicolon => {
207                formatter.write_str("expected semicolon after array")
208            }
209            ErrorKind::ExpectedList => formatter.write_str("expected list"),
210            ErrorKind::ExpectedListComma => formatter.write_str("expected comma in list"),
211            ErrorKind::ExpectedListEnd => formatter.write_str("expected end of list"),
212            ErrorKind::ExpectedGroup => formatter.write_str("expected group"),
213            ErrorKind::ExpectedEquals => formatter.write_str("expected '=' or ':'"),
214            ErrorKind::ExpectedSemicolon => formatter.write_str("expected semicolon"),
215            ErrorKind::ExpectedCommaOrSemicolon => {
216                formatter.write_str("expected comma or semicolon")
217            }
218            ErrorKind::ExpectedGroupEnd => formatter.write_str("expected end of group"),
219            ErrorKind::ExpectedIdentifier => formatter.write_str("expected identifier"),
220            ErrorKind::TrailingCharacters => formatter.write_str("trailing characters"),
221            ErrorKind::HeterogeneousArray => {
222                formatter.write_str("heterogeneous arrays not supported")
223            }
224            ErrorKind::InvalidEscape => formatter.write_str("invalid escape sequence"),
225            ErrorKind::InvalidNumber => formatter.write_str("invalid number"),
226            ErrorKind::KeyMustBeAString => formatter.write_str("map keys must be strings"),
227            ErrorKind::UnitNotSupported => formatter.write_str("unit type not supported"),
228            ErrorKind::TypeNotSupported(ty) => write!(formatter, "type not supported: {}", ty),
229            ErrorKind::IntegerOutOfRange => {
230                formatter.write_str("integer value out of range for target type")
231            }
232            ErrorKind::FloatOutOfRange => {
233                formatter.write_str("float value out of range for target type")
234            }
235            ErrorKind::NonFiniteFloat => {
236                formatter.write_str("non-finite floats (inf, NaN) cannot be represented")
237            }
238            ErrorKind::InvalidSettingName(name) => write!(
239                formatter,
240                "invalid setting name '{}': names must match [A-Za-z*][-A-Za-z0-9_*]*",
241                name
242            ),
243            ErrorKind::InvalidPath(path) => write!(formatter, "invalid path '{}'", path),
244            ErrorKind::PathTraversal(msg) => write!(formatter, "path traversal failed: {}", msg),
245            ErrorKind::ArrayElementNotScalar => {
246                formatter.write_str("arrays may only contain scalar values")
247            }
248            ErrorKind::TopLevelBraces => formatter.write_str(
249                "braces are not allowed around the top level of a configuration; \
250                 write settings directly (name = value;)",
251            ),
252            ErrorKind::UnterminatedComment => {
253                formatter.write_str("unterminated block comment: missing '*/'")
254            }
255            ErrorKind::Include(msg) => write!(formatter, "include error: {}", msg),
256        }
257    }
258}
259
260impl Display for Error {
261    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
262        match (self.line, self.column) {
263            (Some(line), Some(column)) => {
264                write!(formatter, "{} at line {}, column {}", self.kind, line, column)
265            }
266            _ => self.kind.fmt(formatter),
267        }
268    }
269}
270
271impl std::error::Error for Error {}