use crate::css::Value;
use crate::output::Format;
use crate::parser::{ParseError, SourcePos};
use crate::sass::Name;
use crate::value::RangeError;
use std::convert::From;
use std::{fmt, io};
#[derive(Debug)]
pub enum Error {
Input(String, io::Error),
IoError(io::Error),
BadCall(String, SourcePos, Option<SourcePos>),
InvalidFunctionName(SourcePos),
BadValue(String),
BadArgument(Name, String),
BadArguments(String, SourcePos),
ImportLoop(SourcePos, SourcePos),
BadRange(RangeError),
ParseError(ParseError),
UndefinedVariable(SourcePos),
UndefModule(String, SourcePos),
AtError(String, SourcePos),
S(String),
}
impl std::error::Error for Error {}
impl Error {
pub fn bad_value(expected: &str, actual: &Value) -> Self {
Error::BadValue(format!(
"Error: {} is not {}.",
actual.format(Format::introspect()),
expected,
))
}
pub fn bad_arg(
name: Name,
actual: &Value,
problem: &'static str,
) -> Error {
Error::BadArgument(
name,
format!("{} {}", actual.format(Format::introspect()), problem),
)
}
pub fn error<T: AsRef<str>>(msg: T) -> Self {
Error::S(format!("Error: {}", msg.as_ref()))
}
}
impl fmt::Display for Error {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::S(ref s) => write!(out, "{}", s),
Error::Input(ref p, ref e) => {
write!(out, "Failed to read {:?}: {}", p, e)
}
Error::UndefinedVariable(ref pos) => {
writeln!(out, "Error: Undefined variable.")?;
pos.show(out)
}
Error::UndefModule(ref name, ref pos) => {
writeln!(
out,
"Error: There is no module with the namespace {:?}.",
name
)?;
pos.show(out)
}
Error::BadArgument(ref name, ref problem) => {
write!(out, "Error: ${}: {}", name, problem)
}
Error::ParseError(ref err) => err.fmt(out),
Error::ImportLoop(ref pos, ref oldpos) => {
writeln!(out, "Error: This file is already being loaded.")?;
pos.show_detail(out, '^', " new load")?;
writeln!(out)?;
oldpos.show_detail(out, '=', " original load")?;
pos.show_files(out)
}
Error::BadCall(ref msg, ref callpos, ref declpos) => {
msg.fmt(out)?;
writeln!(out)?;
if let Some(declpos) = declpos {
if callpos.same_file_as(declpos) {
show_in_file(
out,
callpos,
" invocation",
declpos,
" declaration",
)?;
} else {
callpos.show_detail(out, '^', " invocation")?;
writeln!(out)?;
declpos.show_detail(out, '=', " declaration")?;
}
callpos.show_files(out)
} else {
callpos.show(out)
}
}
Error::InvalidFunctionName(ref pos) => {
writeln!(out, "Error: Invalid function name.")?;
pos.show(out)
}
Error::AtError(ref value, ref pos) => {
writeln!(out, "Error: {}", value)?;
pos.show(out)
}
Error::BadRange(ref err) => err.fmt(out),
Error::BadValue(ref err) => err.fmt(out),
ref x => write!(out, "{:?}", x),
}
}
}
fn show_in_file(
out: &mut fmt::Formatter,
one: &SourcePos,
one_name: &str,
other: &SourcePos,
other_name: &str,
) -> fmt::Result {
if one < other {
show_in_file2(out, one, one_name, other, other_name)
} else {
show_in_file2(out, other, other_name, one, one_name)
}
}
fn show_in_file2(
out: &mut fmt::Formatter,
first: &SourcePos,
first_name: &str,
second: &SourcePos,
second_name: &str,
) -> fmt::Result {
write!(out, " ,")?;
first.show_inner(out, 3, '=', first_name)?;
write!(out, "... |")?;
second.show_inner(out, 3, '^', second_name)?;
write!(out, " '")?;
Ok(())
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::IoError(e)
}
}
impl From<ParseError> for Error {
fn from(e: ParseError) -> Self {
Error::ParseError(e)
}
}
impl From<RangeError> for Error {
fn from(e: RangeError) -> Self {
Error::BadRange(e)
}
}