use core::fmt::{self, Debug, Display, Write};
use core::iter::FusedIterator;
use core::mem::discriminant;
use alloc::boxed::Box;
use alloc::collections::linked_list::{self, LinkedList};
use alloc::string::ToString;
#[non_exhaustive]
pub enum ErrorKind {
Collision,
Cycle,
Custom(Box<dyn Display + Send + Sync + 'static>),
}
impl ErrorKind {
pub fn is_collision(&self) -> bool {
matches!(self, Self::Collision)
}
pub fn is_cycle(&self) -> bool {
matches!(self, Self::Cycle)
}
pub fn is_custom(&self) -> bool {
matches!(self, Self::Custom(_))
}
}
impl Debug for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Collision => f.write_str("Collision"),
Self::Cycle => f.write_str("Cycle"),
Self::Custom(x) => write!(f, "Custom(\"{x}\")"),
}
}
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Collision => f.write_str("value collision"),
Self::Cycle => f.write_str("cyclic imports"),
Self::Custom(x) => x.fmt(f),
}
}
}
impl PartialEq for ErrorKind {
fn eq(&self, other: &Self) -> bool {
discriminant(self) == discriminant(other)
}
}
impl Eq for ErrorKind {}
#[derive(Clone)]
pub struct Trace {
modules: LinkedList<Box<str>>,
}
impl Debug for Trace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.modules()).finish()
}
}
impl<D> FromIterator<D> for Trace
where
D: Display,
{
fn from_iter<T: IntoIterator<Item = D>>(iter: T) -> Self {
Self {
modules: iter
.into_iter()
.map(|x| x.to_string().into_boxed_str())
.collect(),
}
}
}
impl Trace {
pub const fn new() -> Self {
Self {
modules: LinkedList::new(),
}
}
pub fn len(&self) -> usize {
self.modules.len()
}
pub fn is_empty(&self) -> bool {
self.modules.is_empty()
}
pub fn push_front<D>(&mut self, module: D)
where
D: Display,
{
self.modules.push_front(module.to_string().into_boxed_str());
}
pub fn push_back<D>(&mut self, module: D)
where
D: Display,
{
self.modules.push_back(module.to_string().into_boxed_str());
}
pub fn modules(&self) -> Modules<'_> {
Modules(self.modules.iter())
}
}
pub struct Modules<'a>(linked_list::Iter<'a, Box<str>>);
impl Debug for Modules<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Modules").finish_non_exhaustive()
}
}
impl<'a> Iterator for Modules<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|x| &**x)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl DoubleEndedIterator for Modules<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|x| &**x)
}
}
impl ExactSizeIterator for Modules<'_> {
fn len(&self) -> usize {
self.0.len()
}
}
impl FusedIterator for Modules<'_> {}
#[derive(Clone)]
pub struct Value {
components: LinkedList<Box<str>>,
}
impl Value {
pub const fn new() -> Self {
Self {
components: LinkedList::new(),
}
}
pub fn len(&self) -> usize {
self.components.len()
}
pub fn is_empty(&self) -> bool {
self.components.is_empty()
}
pub fn push_front<D>(&mut self, component: D)
where
D: Display,
{
self.components
.push_front(component.to_string().into_boxed_str());
}
pub fn push_back<D>(&mut self, component: D)
where
D: Display,
{
self.components
.push_back(component.to_string().into_boxed_str());
}
pub fn components(&self) -> Components<'_> {
Components(self.components.iter())
}
}
impl Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sep = if f.align().is_some() { f.fill() } else { '.' };
if !f.alternate() {
f.write_char('\"')?;
}
for (i, component) in self.components().enumerate() {
if i > 0 {
f.write_char(sep)?;
}
f.write_str(component)?;
}
if !f.alternate() {
f.write_char('\"')?;
}
Ok(())
}
}
impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Self as Debug>::fmt(self, f)
}
}
pub struct Components<'a>(linked_list::Iter<'a, Box<str>>);
impl Debug for Components<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Components").finish_non_exhaustive()
}
}
impl<'a> Iterator for Components<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|x| &**x)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl DoubleEndedIterator for Components<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|x| &**x)
}
}
impl ExactSizeIterator for Components<'_> {
fn len(&self) -> usize {
self.0.len()
}
}
impl FusedIterator for Components<'_> {}
#[derive(Debug)]
#[allow(clippy::manual_non_exhaustive)]
pub struct Error {
_priv: (),
pub kind: ErrorKind,
pub trace: Trace,
pub value: Value,
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self::with_kind(kind)
}
}
impl Error {
pub fn collision() -> Self {
Self::with_kind(ErrorKind::Collision)
}
pub fn cycle() -> Self {
Self::with_kind(ErrorKind::Cycle)
}
pub fn custom<T>(msg: T) -> Self
where
T: Display + Send + Sync + 'static,
{
Self::with_kind(ErrorKind::Custom(Box::new(msg)))
}
fn with_kind(kind: ErrorKind) -> Self {
Self {
_priv: (),
kind,
trace: Trace::new(),
value: Value::new(),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.kind, f)?;
if !self.value.is_empty() {
f.write_str(" while evaluating ")?;
Display::fmt(&self.value, f)?;
}
f.write_char('\n')?;
f.write_char('\n')?;
for (i, module) in self.trace.modules().rev().enumerate() {
if i == 0 {
f.write_str(" in ")?;
} else {
f.write_str(" from ")?;
}
f.write_str(module)?;
f.write_char('\n')?;
}
Ok(())
}
}
impl core::error::Error for Error {}