use crate::css::{is_not, InvalidCss};
use crate::input::{LoadError, SourcePos};
use crate::output::{Format, Formatted};
use crate::parser::ParseError;
use crate::ScopeError;
use std::{fmt, io};
#[derive(Debug)]
pub enum Error {
Input(LoadError),
IoError(io::Error),
BadCall(String, SourcePos, Option<SourcePos>),
ImportLoop(bool, SourcePos, Option<SourcePos>),
ParseError(ParseError),
Invalid(Invalid, SourcePos),
S(String),
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::S(ref s) => write!(out, "{s}"),
Self::Input(ref load) => load.fmt(out),
Self::ParseError(ref err) => fmt::Display::fmt(err, out),
Self::ImportLoop(ref module, ref pos, ref oldpos) => {
if *module {
writeln!(
out,
"Module loop: this module is already being loaded."
)?;
} else {
writeln!(out, "This file is already being loaded.")?;
}
if let Some(oldpos) = oldpos {
SourcePos::show_two(
out,
pos,
"new load",
oldpos,
"original load",
)
} else {
pos.show(out)
}
}
Self::BadCall(ref msg, ref callpos, ref declpos) => {
writeln!(out, "{msg}")?;
if let Some(declpos) = declpos {
SourcePos::show_two(
out,
callpos,
"invocation",
declpos,
"declaration",
)
} else {
callpos.show(out)
}
}
Self::Invalid(ref what, ref pos) => {
writeln!(out, "{what}")?;
pos.show(out)
}
Self::IoError(ref err) => err.fmt(out),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Self::IoError(e)
}
}
impl From<fmt::Error> for Error {
fn from(e: fmt::Error) -> Self {
Self::IoError(io::Error::new(io::ErrorKind::Other, e))
}
}
impl From<ParseError> for Error {
fn from(e: ParseError) -> Self {
Self::ParseError(e)
}
}
impl From<LoadError> for Error {
fn from(err: LoadError) -> Self {
Self::Input(err)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Invalid {
FunctionName,
AtRule,
MixinInMixin,
MixinInControl,
FunctionInMixin,
FunctionInControl,
DuplicateArgument,
PositionalArgAfterNamed,
DeclarationOutsideRule,
InNsRule,
GlobalCustomProperty,
GlobalNsProperty,
ConfigBuiltin,
InScope(ScopeError),
AtError(String),
}
impl Invalid {
pub(crate) fn not<'a, T>(v: &'a T, what: &str) -> Self
where
Formatted<'a, T>: std::fmt::Display,
{
Self::AtError(is_not(v, what))
}
pub(crate) fn expected_to<'a, T>(value: &'a T, cond: &str) -> Self
where
Formatted<'a, T>: std::fmt::Display,
{
Self::AtError(format!(
"Expected {} to {}.",
Formatted {
value,
format: Format::introspect()
},
cond
))
}
pub fn at(self, pos: SourcePos) -> Error {
Error::Invalid(self, pos)
}
}
impl std::error::Error for Invalid {}
impl fmt::Display for Invalid {
fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FunctionName => "Invalid function name.".fmt(out),
Self::AtRule => "This at-rule is not allowed here.".fmt(out),
Self::MixinInMixin => {
"Mixins may not contain mixin declarations.".fmt(out)
}
Self::MixinInControl => {
"Mixins may not be declared in control directives.".fmt(out)
}
Self::FunctionInMixin => {
"Mixins may not contain function declarations.".fmt(out)
}
Self::FunctionInControl => {
"Functions may not be declared in control directives."
.fmt(out)
}
Self::DuplicateArgument => "Duplicate argument.".fmt(out),
Self::PositionalArgAfterNamed => {
"Positional arguments must come before keyword arguments."
.fmt(out)
}
Self::DeclarationOutsideRule => {
"Declarations may only be used within style rules.".fmt(out)
}
Self::InNsRule => {
"Only properties are valid inside namespace rules.".fmt(out)
}
Self::GlobalCustomProperty => {
"Global custom property not allowed.".fmt(out)
}
Self::GlobalNsProperty => {
"Global namespaced property not allowed.".fmt(out)
}
Self::ConfigBuiltin => {
"Built-in modules can\'t be configured.".fmt(out)
}
Self::InScope(err) => err.fmt(out),
Self::AtError(msg) => msg.fmt(out),
}
}
}
pub(crate) trait ResultPos<T> {
fn at(self, pos: &SourcePos) -> Result<T, Error>;
fn no_pos(self) -> Result<T, Error>;
}
impl<T> ResultPos<T> for Result<T, Invalid> {
fn at(self, pos: &SourcePos) -> Result<T, Error> {
self.map_err(|e| e.at(pos.clone()))
}
fn no_pos(self) -> Result<T, Error> {
self.map_err(|e| Error::S(e.to_string()))
}
}
impl<T> ResultPos<T> for Result<T, InvalidCss> {
fn at(self, pos: &SourcePos) -> Result<T, Error> {
self.map_err(|e| Invalid::AtError(e.to_string()).at(pos.clone()))
}
fn no_pos(self) -> Result<T, Error> {
self.map_err(|e| Error::S(e.to_string()))
}
}