serde-libconfigasaurus 0.4.0

Serde serialization and deserialization for libconfig format
Documentation
//! Error types for libconfig serialization and deserialization

use std::fmt::{self, Display};

use serde::{de, ser};

/// A Result type alias where the error is `libconfig::Error`
pub type Result<T> = std::result::Result<T, Error>;

/// An error that occurred while serializing or deserializing libconfig data.
///
/// Errors produced while parsing text carry the line and column (1-based) at
/// which the problem was found; inspect them with [`Error::line`] and
/// [`Error::column`]. The underlying cause is available as [`Error::kind`].
#[derive(Debug)]
pub struct Error {
    kind: ErrorKind,
    line: Option<usize>,
    column: Option<usize>,
}

impl Error {
    /// The kind of error that occurred.
    pub fn kind(&self) -> &ErrorKind {
        &self.kind
    }

    /// The 1-based line number where a parse error occurred, if known.
    pub fn line(&self) -> Option<usize> {
        self.line
    }

    /// The 1-based column number where a parse error occurred, if known.
    pub fn column(&self) -> Option<usize> {
        self.column
    }

    /// Attaches a source location, unless one is already present.
    pub(crate) fn with_location(mut self, line: usize, column: usize) -> Self {
        if self.line.is_none() {
            self.line = Some(line);
            self.column = Some(column);
        }
        self
    }
}

impl From<ErrorKind> for Error {
    fn from(kind: ErrorKind) -> Self {
        Error {
            kind,
            line: None,
            column: None,
        }
    }
}

/// The specific kind of error underlying an [`Error`].
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorKind {
    /// A custom error message from Serde
    Message(String),

    /// An I/O error, carrying the underlying error's message
    Io(String),

    /// Unexpected end of input
    Eof,

    /// Syntax error in the input
    Syntax,

    /// Expected a boolean value
    ExpectedBoolean,

    /// Expected an integer value
    ExpectedInteger,

    /// Expected a float value
    ExpectedFloat,

    /// Expected a string value
    ExpectedString,

    /// Expected an array
    ExpectedArray,

    /// Expected a comma in an array
    ExpectedArrayComma,

    /// Expected end of array
    ExpectedArrayEnd,

    /// Expected a semicolon after array
    ExpectedArraySemicolon,

    /// Expected a list
    ExpectedList,

    /// Expected a comma in a list
    ExpectedListComma,

    /// Expected end of list
    ExpectedListEnd,

    /// Expected a group (struct)
    ExpectedGroup,

    /// Expected an equals sign or colon
    ExpectedEquals,

    /// Expected a semicolon
    ExpectedSemicolon,

    /// Expected a comma or semicolon
    ExpectedCommaOrSemicolon,

    /// Expected end of group
    ExpectedGroupEnd,

    /// Expected an identifier
    ExpectedIdentifier,

    /// Trailing characters after parsing
    TrailingCharacters,

    /// Heterogeneous arrays are not supported
    HeterogeneousArray,

    /// Invalid escape sequence
    InvalidEscape,

    /// Invalid number format
    InvalidNumber,

    /// Maps with non-string keys are not supported
    KeyMustBeAString,

    /// Unit type not supported at top level
    UnitNotSupported,

    /// Type not supported
    TypeNotSupported(String),

    /// Integer value does not fit in the target type
    IntegerOutOfRange,

    /// Float value does not fit in the target type
    FloatOutOfRange,

    /// Non-finite floats (inf, NaN) cannot be represented in libconfig
    NonFiniteFloat,

    /// Setting name does not match `[A-Za-z*][-A-Za-z0-9_*]*`
    InvalidSettingName(String),

    /// Path passed to `lookup`/`set`/`remove` is malformed
    InvalidPath(String),

    /// A path traversal step failed (wrong container type or missing index)
    PathTraversal(String),

    /// Arrays may only contain scalar values
    ArrayElementNotScalar,

    /// Braces around the top level of a configuration are not allowed
    /// (grammar: a configuration is a setting list; braces delimit nested
    /// groups only)
    TopLevelBraces,

    /// A `/*` block comment was never closed with `*/` (section 2.10
    /// defines block comments as delimited pairs)
    UnterminatedComment,

    /// Error processing an `@include` directive
    Include(String),
}

impl ser::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        ErrorKind::Message(msg.to_string()).into()
    }
}

impl de::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        ErrorKind::Message(msg.to_string()).into()
    }
}

impl Display for ErrorKind {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorKind::Message(msg) => formatter.write_str(msg),
            ErrorKind::Io(msg) => write!(formatter, "i/o error: {}", msg),
            ErrorKind::Eof => formatter.write_str("unexpected end of input"),
            ErrorKind::Syntax => formatter.write_str("syntax error"),
            ErrorKind::ExpectedBoolean => formatter.write_str("expected boolean"),
            ErrorKind::ExpectedInteger => formatter.write_str("expected integer"),
            ErrorKind::ExpectedFloat => formatter.write_str("expected float"),
            ErrorKind::ExpectedString => formatter.write_str("expected string"),
            ErrorKind::ExpectedArray => formatter.write_str("expected array"),
            ErrorKind::ExpectedArrayComma => formatter.write_str("expected comma in array"),
            ErrorKind::ExpectedArrayEnd => formatter.write_str("expected end of array"),
            ErrorKind::ExpectedArraySemicolon => {
                formatter.write_str("expected semicolon after array")
            }
            ErrorKind::ExpectedList => formatter.write_str("expected list"),
            ErrorKind::ExpectedListComma => formatter.write_str("expected comma in list"),
            ErrorKind::ExpectedListEnd => formatter.write_str("expected end of list"),
            ErrorKind::ExpectedGroup => formatter.write_str("expected group"),
            ErrorKind::ExpectedEquals => formatter.write_str("expected '=' or ':'"),
            ErrorKind::ExpectedSemicolon => formatter.write_str("expected semicolon"),
            ErrorKind::ExpectedCommaOrSemicolon => {
                formatter.write_str("expected comma or semicolon")
            }
            ErrorKind::ExpectedGroupEnd => formatter.write_str("expected end of group"),
            ErrorKind::ExpectedIdentifier => formatter.write_str("expected identifier"),
            ErrorKind::TrailingCharacters => formatter.write_str("trailing characters"),
            ErrorKind::HeterogeneousArray => {
                formatter.write_str("heterogeneous arrays not supported")
            }
            ErrorKind::InvalidEscape => formatter.write_str("invalid escape sequence"),
            ErrorKind::InvalidNumber => formatter.write_str("invalid number"),
            ErrorKind::KeyMustBeAString => formatter.write_str("map keys must be strings"),
            ErrorKind::UnitNotSupported => formatter.write_str("unit type not supported"),
            ErrorKind::TypeNotSupported(ty) => write!(formatter, "type not supported: {}", ty),
            ErrorKind::IntegerOutOfRange => {
                formatter.write_str("integer value out of range for target type")
            }
            ErrorKind::FloatOutOfRange => {
                formatter.write_str("float value out of range for target type")
            }
            ErrorKind::NonFiniteFloat => {
                formatter.write_str("non-finite floats (inf, NaN) cannot be represented")
            }
            ErrorKind::InvalidSettingName(name) => write!(
                formatter,
                "invalid setting name '{}': names must match [A-Za-z*][-A-Za-z0-9_*]*",
                name
            ),
            ErrorKind::InvalidPath(path) => write!(formatter, "invalid path '{}'", path),
            ErrorKind::PathTraversal(msg) => write!(formatter, "path traversal failed: {}", msg),
            ErrorKind::ArrayElementNotScalar => {
                formatter.write_str("arrays may only contain scalar values")
            }
            ErrorKind::TopLevelBraces => formatter.write_str(
                "braces are not allowed around the top level of a configuration; \
                 write settings directly (name = value;)",
            ),
            ErrorKind::UnterminatedComment => {
                formatter.write_str("unterminated block comment: missing '*/'")
            }
            ErrorKind::Include(msg) => write!(formatter, "include error: {}", msg),
        }
    }
}

impl Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match (self.line, self.column) {
            (Some(line), Some(column)) => {
                write!(formatter, "{} at line {}, column {}", self.kind, line, column)
            }
            _ => self.kind.fmt(formatter),
        }
    }
}

impl std::error::Error for Error {}