spectec_ast_decode 1.0.0

Trait for decoding SpecTec AST S-expressions
Documentation
use std::{num::ParseIntError, str::ParseBoolError};

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ErrorKind {
    #[error("Unrecognised atom symbol: {0}")]
    UnrecognisedAtomSymbol(String),
    #[error("Unrecognised node symbol: {0}")]
    UnrecognisedNodeSymbol(String),
    #[error("Required another S-expression but stream is empty")]
    RequiredMissingSExpr(),
    #[error("Could not decode from S-Expression: {0}")]
    CannotDecodeSExpr(String),
    #[error("Extra unparsed S-expression remaining: {0}")]
    UnparsedSExpr(String),
    #[error("Error parsing a bool: {0}")]
    ParseBoolError(#[from] std::str::ParseBoolError),
    #[error("Error parsing an int: {0}")]
    ParseIntError(#[from] std::num::ParseIntError),
    #[error("{0}")]
    Wrapped(#[from] Box<Error>),
}

#[derive(Error, Debug)]
pub struct Error {
    #[source]
    pub kind: ErrorKind,
    pub variant: Option<String>,
    pub field: Option<String>,
    pub decoding: &'static str,
}

impl Error {
    #[must_use]
    pub(crate) fn new<T: crate::Decode>(kind: ErrorKind) -> Self {
        Self {
            kind,
            variant: None,
            field: None,
            decoding: std::any::type_name::<T>(),
        }
    }

    #[must_use]
    pub fn with_variant<S: Into<String>>(mut self, variant: S) -> Self {
        self.variant = Some(variant.into());
        self
    }

    #[must_use]
    pub fn with_field<S: Into<String>>(mut self, field: S) -> Self {
        self.field = Some(field.into());
        self
    }

    #[must_use]
    pub fn unrecognised_atom_symbol<T: crate::Decode, S: Into<String>>(symbol: S) -> Self {
        Self::new::<T>(ErrorKind::UnrecognisedAtomSymbol(symbol.into()))
    }

    #[must_use]
    pub fn unrecognised_node_symbol<T: crate::Decode, S: Into<String>>(symbol: S) -> Self {
        Self::new::<T>(ErrorKind::UnrecognisedNodeSymbol(symbol.into()))
    }

    #[must_use]
    pub fn required_missing_sexpr<T: crate::Decode>() -> Self {
        Self::new::<T>(ErrorKind::RequiredMissingSExpr())
    }

    #[must_use]
    pub fn cannot_decode_sexpr<T: crate::Decode>(sexpr: &sexpr_parse::SExprItem) -> Self {
        Self::new::<T>(ErrorKind::CannotDecodeSExpr(format!("{sexpr:?}")))
    }

    #[must_use]
    pub fn unparsed_sexpr<T: crate::Decode>(sexpr: &sexpr_parse::SExprItem) -> Self {
        Self::new::<T>(ErrorKind::UnparsedSExpr(format!("{sexpr:?}")))
    }

    #[must_use]
    pub fn parse_bool_err<T: crate::Decode>(from: ParseBoolError) -> Self {
        Self::new::<T>(ErrorKind::from(from))
    }

    #[must_use]
    pub fn parse_int_err<T: crate::Decode>(from: ParseIntError) -> Self {
        Self::new::<T>(ErrorKind::from(from))
    }

    #[must_use]
    pub fn wrapped<T: crate::Decode>(from: Error) -> Self {
        Self::new::<T>(ErrorKind::from(Box::new(from)))
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Error decoding {}", self.decoding)?;
        if let Some(variant) = &self.variant {
            write!(f, "::{variant}")?;
        }
        if let Some(field) = &self.field {
            write!(f, ".{field}")?;
        }
        write!(f, ": {}", self.kind)
    }
}

pub type Result<T> = core::result::Result<T, Error>;