use std::fmt;
use crate::ffi;
#[derive(Debug)]
#[cfg_attr(not(feature = "serde"), allow(dead_code))]
#[non_exhaustive]
pub enum Error {
InvalidArgument,
Parse(ParseError),
OutOfMemory,
UnsupportedFormat,
NotFound,
Internal,
Utf8,
Number(String),
Message(String),
MissingField { field: &'static str, ty: &'static str },
ExpectedMapping { ty: &'static str },
UnknownVariant { enum_name: &'static str, got: String },
WrongSeqLen { label: &'static str, expected: usize, got: usize },
Static(&'static str),
TypeMismatch { expected: &'static str, found: &'static str },
IntOutOfRange { ty: &'static str },
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ParseError {
pub message: String,
pub byte_offset: Option<usize>,
pub line: Option<u32>,
pub column: Option<u32>,
}
impl ParseError {
pub(crate) fn from_ffi(e: &ffi::FigError) -> Self {
let len = e.message_len.min(e.message.len());
let message = String::from_utf8_lossy(&e.message[..len]).into_owned();
ParseError {
message,
byte_offset: (e.byte_offset != 0).then_some(e.byte_offset),
line: (e.line != 0).then_some(e.line),
column: (e.column != 0).then_some(e.column),
}
}
pub(crate) fn generic() -> Self {
ParseError {
message: String::from("failed to parse input"),
byte_offset: None,
line: None,
column: None,
}
}
}
impl Error {
#[cold]
#[inline(never)]
pub fn missing_field(field: &'static str, ty: &'static str) -> Self {
Error::MissingField { field, ty }
}
#[cold]
#[inline(never)]
pub fn expected_mapping(ty: &'static str) -> Self {
Error::ExpectedMapping { ty }
}
#[cold]
#[inline(never)]
pub fn unknown_variant(enum_name: &'static str, got: &str) -> Self {
Error::UnknownVariant { enum_name, got: got.to_string() }
}
#[cold]
#[inline(never)]
pub fn wrong_seq_len(label: &'static str, expected: usize, got: usize) -> Self {
Error::WrongSeqLen { label, expected, got }
}
#[cold]
#[inline(never)]
pub fn msg_static(msg: &'static str) -> Self {
Error::Static(msg)
}
#[cold]
#[inline(never)]
pub fn type_mismatch(expected: &'static str, found: &'static str) -> Self {
Error::TypeMismatch { expected, found }
}
#[cold]
#[inline(never)]
pub fn int_out_of_range(ty: &'static str) -> Self {
Error::IntOutOfRange { ty }
}
pub(crate) fn from_status(status: ffi::FigStatus) -> Result<(), Self> {
match status.0 {
ffi::FigStatus::OK => Ok(()),
ffi::FigStatus::INVALID_ARGUMENT => Err(Self::InvalidArgument),
ffi::FigStatus::PARSE_ERROR => Err(Self::Parse(ParseError::generic())),
ffi::FigStatus::OUT_OF_MEMORY => Err(Self::OutOfMemory),
ffi::FigStatus::UNSUPPORTED_FORMAT => Err(Self::UnsupportedFormat),
ffi::FigStatus::NOT_FOUND => Err(Self::NotFound),
_ => Err(Self::Internal),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidArgument => f.write_str("invalid argument"),
Error::Parse(e) => {
write!(f, "failed to parse input: {}", e.message)?;
match (e.line, e.column) {
(Some(l), Some(c)) => write!(f, " (line {l}, column {c})"),
_ => match e.byte_offset {
Some(off) => write!(f, " (byte offset {off})"),
None => Ok(()),
},
}
}
Error::OutOfMemory => f.write_str("out of memory"),
Error::UnsupportedFormat => f.write_str("unsupported format"),
Error::NotFound => f.write_str("path or region not found"),
Error::Internal => f.write_str("internal error"),
Error::Utf8 => f.write_str("scalar was not valid UTF-8"),
Error::Number(raw) => write!(f, "invalid number: {raw}"),
Error::Message(msg) => f.write_str(msg),
Error::MissingField { field, ty } => {
write!(f, "missing field `{field}` while building `{ty}`")
}
Error::ExpectedMapping { ty } => write!(f, "expected a mapping to build `{ty}`"),
Error::UnknownVariant { enum_name, got } => {
write!(f, "unknown variant `{got}` for enum `{enum_name}`")
}
Error::WrongSeqLen { label, expected, got } => {
write!(f, "expected {expected} element(s) for `{label}`, found {got}")
}
Error::Static(msg) => f.write_str(msg),
Error::TypeMismatch { expected, found } => {
write!(f, "expected {expected}, found {found}")
}
Error::IntOutOfRange { ty } => {
write!(f, "integer out of range for {ty}")
}
}
}
}
impl std::error::Error for Error {}
#[cfg(feature = "serde")]
impl serde::de::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Error::Message(msg.to_string())
}
}
#[cfg(feature = "serde")]
impl serde::ser::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Error::Message(msg.to_string())
}
}