jsonx 0.1.0

A serde-enabled implementation of the JSONX extended-JSON format: typed value constructors, unquoted keys, and trailing commas.
Documentation
//! Error and result types for `jsonx`.

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

/// Alias for a `Result` with the crate's [`Error`] type.
pub type Result<T> = std::result::Result<T, Error>;

/// An error produced while parsing, deserializing, serializing, or doing I/O.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
    /// The input was not valid JSONX. Carries a human-readable message and the
    /// byte offset (into the input) where the problem was detected.
    Syntax {
        /// Human readable description of what went wrong.
        msg: String,
        /// Byte offset into the input where the error was detected.
        offset: usize,
    },
    /// The input ended before a complete value could be parsed.
    Eof,
    /// A complete value was parsed but non-whitespace data followed it.
    ///
    /// `offset` is the byte offset of the first trailing byte. This is what you
    /// inspect to implement non-greedy decoding (see [`crate::from_str_partial`]).
    TrailingData {
        /// Byte offset of the first byte that follows the top-level value.
        offset: usize,
    },
    /// A semantic error originating from a `serde` `Serialize`/`Deserialize`
    /// implementation, or another value-level problem (e.g. a missing field).
    Message(String),
    /// An I/O error occurred while writing to a [`std::io::Write`].
    Io(io::Error),
}

impl Error {
    pub(crate) fn syntax(msg: impl Into<String>, offset: usize) -> Self {
        Error::Syntax {
            msg: msg.into(),
            offset,
        }
    }

    pub(crate) fn message(msg: impl Into<String>) -> Self {
        Error::Message(msg.into())
    }

    /// Returns the byte offset associated with the error, if any.
    pub fn offset(&self) -> Option<usize> {
        match self {
            Error::Syntax { offset, .. } | Error::TrailingData { offset } => Some(*offset),
            _ => None,
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Syntax { msg, offset } => write!(f, "{msg} (at byte offset {offset})"),
            Error::Eof => f.write_str("unexpected end of input"),
            Error::TrailingData { offset } => {
                write!(f, "trailing data after top-level value (at byte offset {offset})")
            }
            Error::Message(msg) => f.write_str(msg),
            Error::Io(err) => Display::fmt(err, f),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::Io(err) => Some(err),
            _ => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::Io(err)
    }
}

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

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