1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
use crate::css::InvalidCss;
use crate::input::{LoadError, SourcePos};
use crate::parser::ParseError;
use crate::value::RangeError;
use crate::ScopeError;
use std::convert::From;
use std::{fmt, io};
/// Many functions in rsass that returns a Result uses this Error type.
///
/// Other errors in rsass, such as [`CallError`][crate::sass::CallError] or
/// [`Invalid`] can be converted to this Error type, often with
/// a method providing some context rather than with just an `Into`
/// implementation.
/// E.g. [`CallError::called_from`][crate::sass::CallError::called_from]
/// also takes a [`SourcePos`] and a function name, defining the call
/// that went wrong.
pub enum Error {
/// Failed to load a file.
Input(LoadError),
/// An IO error without specifying a path.
///
/// This is (probably) an error writing output.
IoError(io::Error),
/// A failed function call, with call- and optionally declaration
/// position.
BadCall(String, SourcePos, Option<SourcePos>),
/// Tried to import file at pos while already importing it at pos.
///
/// The bool is true for a used module and false for an import.
ImportLoop(bool, SourcePos, Option<SourcePos>),
/// A range error
BadRange(RangeError),
/// Error parsing sass data.
ParseError(ParseError),
/// Something bad at a specific position.
Invalid(Invalid, SourcePos),
/// Fallback error type.
///
/// This just contains a string with some message.
///
/// Note: This variant should probably be removed in the future.
S(String),
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
write!(out, "Error: {self:?}")
}
}
impl fmt::Debug for Error {
fn fmt(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::S(ref s) => write!(out, "{s}"),
Error::Input(ref load) => load.fmt(out),
Error::ParseError(ref err) => fmt::Display::fmt(err, out),
Error::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)
}
}
Error::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)
}
}
Error::Invalid(ref what, ref pos) => {
writeln!(out, "{what}")?;
pos.show(out)
}
Error::BadRange(ref err) => err.fmt(out),
Error::IoError(ref err) => err.fmt(out),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::IoError(e)
}
}
impl From<fmt::Error> for Error {
fn from(e: fmt::Error) -> Self {
Error::IoError(io::Error::new(io::ErrorKind::Other, 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)
}
}
impl From<LoadError> for Error {
fn from(err: LoadError) -> Self {
Error::Input(err)
}
}
/// Something invalid.
///
/// Should be combined with a position to get an [Error].
#[derive(Debug)]
#[non_exhaustive]
pub enum Invalid {
/// Tried to declare a function with a forbidden name.
FunctionName,
/// This at rule is not allowed here.
AtRule,
/// Mixins may not contain mixin declarations.
MixinInMixin,
/// Mixins may not be declared in control directives.
MixinInControl,
/// Mixins may not contain function declarations.
FunctionInMixin,
/// Functions may not be declared in control directives.
FunctionInControl,
/// Duplicate argument.
DuplicateArgument,
/// Positional arguments must come before keyword arguments.
PositionalArgAfterNamed,
/// Declarations may only be used within style rules.
DeclarationOutsideRule,
/// Only properties are valid inside namespace rules.
InNsRule,
/// Global custom property not allowed.
GlobalCustomProperty,
/// Global namespaced property not allowed.
GlobalNsProperty,
/// Built-in modules can't be configured.
ConfigBuiltin,
/// Some invalid scope operation.
InScope(ScopeError),
/// An `@error` reached.
AtError(String),
}
impl Invalid {
/// Combine this with a position to get an [`Error`].
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 {
Invalid::FunctionName => "Invalid function name.".fmt(out),
Invalid::AtRule => "This at-rule is not allowed here.".fmt(out),
Invalid::MixinInMixin => {
"Mixins may not contain mixin declarations.".fmt(out)
}
Invalid::MixinInControl => {
"Mixins may not be declared in control directives.".fmt(out)
}
Invalid::FunctionInMixin => {
"Mixins may not contain function declarations.".fmt(out)
}
Invalid::FunctionInControl => {
"Functions may not be declared in control directives."
.fmt(out)
}
Invalid::DuplicateArgument => "Duplicate argument.".fmt(out),
Invalid::PositionalArgAfterNamed => {
"Positional arguments must come before keyword arguments."
.fmt(out)
}
Invalid::DeclarationOutsideRule => {
"Declarations may only be used within style rules.".fmt(out)
}
Invalid::InNsRule => {
"Only properties are valid inside namespace rules.".fmt(out)
}
Invalid::GlobalCustomProperty => {
"Global custom property not allowed.".fmt(out)
}
Invalid::GlobalNsProperty => {
"Global namespaced property not allowed.".fmt(out)
}
Invalid::ConfigBuiltin => {
"Built-in modules can\'t be configured.".fmt(out)
}
Invalid::InScope(err) => err.fmt(out),
Invalid::AtError(msg) => msg.fmt(out),
}
}
}
pub(crate) trait ResultPos<T> {
/// Someting bad happened at a given source position.
fn at(self, pos: &SourcePos) -> Result<T, Error>;
/// Something bad happened, but we don't know the position.
/// This is probably room for improvement in rsass error handling.
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()))
}
}