use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt::{self, Display};
use facet_reflect::{ReflectError, Span};
#[derive(Debug, Clone)]
pub struct TomlError {
pub kind: TomlErrorKind,
pub span: Option<Span>,
pub source_code: Option<String>,
}
impl Display for TomlError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
impl std::error::Error for TomlError {}
impl TomlError {
pub const fn new(kind: TomlErrorKind, span: Span) -> Self {
TomlError {
kind,
span: Some(span),
source_code: None,
}
}
pub const fn without_span(kind: TomlErrorKind) -> Self {
TomlError {
kind,
span: None,
source_code: None,
}
}
pub fn with_source(mut self, source: &str) -> Self {
self.source_code = Some(source.to_string());
self
}
}
#[derive(Debug, Clone)]
pub enum TomlErrorKind {
Parse(String),
UnexpectedType {
expected: &'static str,
got: &'static str,
},
UnexpectedEof {
expected: &'static str,
},
UnknownField {
field: String,
expected: Vec<&'static str>,
suggestion: Option<&'static str>,
},
MissingField {
field: &'static str,
table_start: Option<Span>,
table_end: Option<Span>,
},
InvalidValue {
message: String,
},
Reflect(ReflectError),
NumberOutOfRange {
value: String,
target_type: &'static str,
},
DuplicateKey {
key: String,
},
InvalidUtf8(core::str::Utf8Error),
Solver(String),
Serialize(String),
}
impl Display for TomlErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TomlErrorKind::Parse(msg) => write!(f, "parse error: {msg}"),
TomlErrorKind::UnexpectedType { expected, got } => {
write!(f, "type mismatch: expected {expected}, got {got}")
}
TomlErrorKind::UnexpectedEof { expected } => {
write!(f, "unexpected end of input, expected {expected}")
}
TomlErrorKind::UnknownField {
field,
expected,
suggestion,
} => {
write!(f, "unknown field `{field}`, expected one of: {expected:?}")?;
if let Some(suggested) = suggestion {
write!(f, " (did you mean `{suggested}`?)")?;
}
Ok(())
}
TomlErrorKind::MissingField { field, .. } => {
write!(f, "missing required field `{field}`")
}
TomlErrorKind::InvalidValue { message } => {
write!(f, "invalid value: {message}")
}
TomlErrorKind::Reflect(e) => write!(f, "reflection error: {e}"),
TomlErrorKind::NumberOutOfRange { value, target_type } => {
write!(f, "number `{value}` out of range for {target_type}")
}
TomlErrorKind::DuplicateKey { key } => {
write!(f, "duplicate key `{key}`")
}
TomlErrorKind::InvalidUtf8(e) => write!(f, "invalid UTF-8 sequence: {e}"),
TomlErrorKind::Solver(msg) => write!(f, "solver error: {msg}"),
TomlErrorKind::Serialize(msg) => write!(f, "serialization error: {msg}"),
}
}
}
impl TomlErrorKind {
pub const fn code(&self) -> &'static str {
match self {
TomlErrorKind::Parse(_) => "toml::parse",
TomlErrorKind::UnexpectedType { .. } => "toml::type_mismatch",
TomlErrorKind::UnexpectedEof { .. } => "toml::unexpected_eof",
TomlErrorKind::UnknownField { .. } => "toml::unknown_field",
TomlErrorKind::MissingField { .. } => "toml::missing_field",
TomlErrorKind::InvalidValue { .. } => "toml::invalid_value",
TomlErrorKind::Reflect(_) => "toml::reflect",
TomlErrorKind::NumberOutOfRange { .. } => "toml::number_out_of_range",
TomlErrorKind::DuplicateKey { .. } => "toml::duplicate_key",
TomlErrorKind::InvalidUtf8(_) => "toml::invalid_utf8",
TomlErrorKind::Solver(_) => "toml::solver",
TomlErrorKind::Serialize(_) => "toml::serialize",
}
}
pub fn label(&self) -> String {
match self {
TomlErrorKind::Parse(msg) => format!("parse error: {msg}"),
TomlErrorKind::UnexpectedType { expected, got } => {
format!("expected {expected}, got {got}")
}
TomlErrorKind::UnexpectedEof { expected } => format!("expected {expected}"),
TomlErrorKind::UnknownField {
field, suggestion, ..
} => {
if let Some(suggested) = suggestion {
format!("unknown field '{field}' - did you mean '{suggested}'?")
} else {
format!("unknown field '{field}'")
}
}
TomlErrorKind::MissingField { field, .. } => format!("missing field '{field}'"),
TomlErrorKind::InvalidValue { .. } => "invalid value".into(),
TomlErrorKind::Reflect(_) => "reflection error".into(),
TomlErrorKind::NumberOutOfRange { target_type, .. } => {
format!("out of range for {target_type}")
}
TomlErrorKind::DuplicateKey { key } => format!("duplicate key '{key}'"),
TomlErrorKind::InvalidUtf8(_) => "invalid UTF-8".into(),
TomlErrorKind::Solver(_) => "solver error".into(),
TomlErrorKind::Serialize(_) => "serialization error".into(),
}
}
}
impl From<ReflectError> for TomlError {
fn from(err: ReflectError) -> Self {
TomlError {
kind: TomlErrorKind::Reflect(err),
span: None,
source_code: None,
}
}
}
#[allow(dead_code)]
pub type Result<T> = core::result::Result<T, TomlError>;