trait-aliases 0.3.0

Trait aliases.
Documentation
use std::{cell::RefCell, fmt::Display, thread::panicking};

use quote::ToTokens;
use syn::{Error, Result};

pub const CHECKED: &str = "already checked for errors";
pub const FORGOT: &str = "forgot to check for errors";

pub struct Context {
    errors: RefCell<Option<Vec<Error>>>,
}

impl Context {
    pub const fn new() -> Self {
        Self {
            errors: RefCell::new(Some(Vec::new())),
        }
    }

    pub fn checked(&self) -> bool {
        self.errors.borrow().is_none()
    }

    pub fn error_spanned_by<T: ToTokens, M: Display>(&self, tokens: T, message: M) {
        self.error(Error::new_spanned(tokens, message));
    }

    pub fn error(&self, error: Error) {
        self.errors
            .borrow_mut()
            .as_mut()
            .expect(CHECKED)
            .push(error);
    }

    pub fn check_error(self) -> Option<Error> {
        self.errors
            .borrow_mut()
            .take()
            .expect(CHECKED)
            .into_iter()
            .reduce(|mut combined, error| {
                combined.combine(error);

                combined
            })
    }

    pub fn check(self) -> Result<()> {
        self.check_error().map_or(Ok(()), Err)
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        assert!(panicking() || self.checked(), "{FORGOT}");
    }
}

pub trait OrReport: Sized {
    type Value;

    fn or_report(self, context: &Context) -> Option<Self::Value>;
}

impl<T> OrReport for Result<T> {
    type Value = T;

    fn or_report(self, context: &Context) -> Option<Self::Value> {
        match self {
            Ok(value) => Some(value),
            Err(error) => {
                context.error(error);

                None
            }
        }
    }
}