#![warn( clippy::all, clippy::restriction, clippy::pedantic, clippy::nursery, clippy::cargo )] #![deny(missing_debug_implementations)] #![allow( // explicit return makes some things look ugly clippy::implicit_return, // Self { .. } is less explicit than Foo { .. } clippy::use_self, // this is way too pedantic -- some things don't need docs! clippy::missing_docs_in_private_items, // unreachable!() has many valid use cases clippy::unreachable, // _ => {} has many valid use cases clippy::wildcard_enum_match_arm, // .expect() has many valid use cases, like when we know a value is `Some(..)` clippy::option_expect_used, // this is too pedantic -- we are allowed to add numbers! clippy::integer_arithmetic, // this is too pedantic for now -- the library is changing too quickly for // good docs to be written clippy::missing_errors_doc, // this incorrectly results in errors for types that derive `Debug` // https://github.com/rust-lang/rust-clippy/issues/4980 clippy::let_underscore_must_use, // this is too pedantic -- it results in some names being less explicit // than they should clippy::module_name_repetitions, clippy::option_unwrap_used, // this is too pedantic -- it is sometimes useful to break up `impl`s clippy::multiple_inherent_impl, // temporarily allowed while under heavy development. // eventually these allows should be refactored away // to no longer be necessary clippy::as_conversions, clippy::todo, clippy::too_many_lines, clippy::panic, clippy::result_unwrap_used, clippy::result_expect_used, clippy::cast_possible_truncation, clippy::single_match_else, clippy::indexing_slicing, clippy::match_same_arms, clippy::or_fun_call, )] #![cfg_attr(feature = "nightly", feature(track_caller))] // todo! handle erroring on styles at the toplevel use std::fmt::{self, Display}; use std::fs; use std::io::Write; use std::iter::{Iterator, Peekable}; use std::path::Path; use crate::atrule::{AtRule, AtRuleKind}; use crate::common::{Keyword, Op, Pos, Scope, Symbol, Whitespace}; use crate::css::Css; use crate::error::SassError; pub use crate::error::SassResult; use crate::format::PrettyPrinter; use crate::function::Function; use crate::imports::import; use crate::lexer::Lexer; use crate::mixin::{eat_include, Mixin}; use crate::selector::{Attribute, Selector}; use crate::style::Style; use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace, VariableDecl}; use crate::value::Value; mod args; mod atrule; mod builtin; mod color; mod common; mod css; mod error; mod format; mod function; mod imports; mod lexer; mod mixin; mod selector; mod style; mod units; mod utils; mod value; #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct Token { pos: Pos, pub kind: TokenKind, } impl Token { pub fn equals_symbol(&self, s: Symbol) -> bool { self.kind.equals_symbol(s) } } impl IsWhitespace for Token { fn is_whitespace(&self) -> bool { if let TokenKind::Whitespace(_) = self.kind { return true; } false } } impl IsWhitespace for &Token { fn is_whitespace(&self) -> bool { if let TokenKind::Whitespace(_) = self.kind { return true; } false } } impl IsComment for Token { fn is_comment(&self) -> bool { if let TokenKind::MultilineComment(_) = self.kind { return true; } false } } impl IsComment for &Token { fn is_comment(&self) -> bool { if let TokenKind::MultilineComment(_) = self.kind { return true; } false } } #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) enum TokenKind { Ident(String), Symbol(Symbol), AtRule(AtRuleKind), Keyword(Keyword), Number(String), Whitespace(Whitespace), Variable(String), Attribute(Attribute), Op(Op), MultilineComment(String), Interpolation, } impl TokenKind { pub fn equals_symbol(&self, s: Symbol) -> bool { self == &TokenKind::Symbol(s) } } impl Display for TokenKind { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TokenKind::Ident(s) | TokenKind::Number(s) => write!(f, "{}", s), TokenKind::Symbol(s) => write!(f, "{}", s), TokenKind::AtRule(s) => write!(f, "{}", s), TokenKind::Op(s) => write!(f, "{}", s), TokenKind::Whitespace(s) => write!(f, "{}", s), TokenKind::Attribute(s) => write!(f, "{}", s), TokenKind::Keyword(kw) => write!(f, "{}", kw), TokenKind::MultilineComment(s) => write!(f, "/*{}*/", s), TokenKind::Variable(s) => write!(f, "{}", s), TokenKind::Interpolation => { panic!("we don't want to format TokenKind::Interpolation using Display") } } } } /// Represents a parsed SASS stylesheet with nesting #[derive(Debug, Clone)] pub struct StyleSheet(Vec); #[derive(Clone, Debug)] pub(crate) enum Stmt { /// A [`Style`](/grass/style/struct.Style) Style(Style), /// A [`RuleSet`](/grass/struct.RuleSet.html) RuleSet(RuleSet), /// A multiline comment: `/* foo bar */` MultilineComment(String), /// A CSS rule: `@charset "UTF-8";` AtRule(AtRule), } /// Represents a single rule set. Rule sets can contain other rule sets /// /// ```scss /// a { /// color: blue; /// b { /// color: red; /// } /// } /// ``` #[derive(Clone, Debug)] pub(crate) struct RuleSet { selector: Selector, rules: Vec, // potential optimization: we don't *need* to own the selector super_selector: Selector, } /// An intermediate representation of what are essentially single lines /// todo! rename this #[derive(Clone, Debug)] enum Expr { /// A style: `color: red` Style(Style), /// Several styles Styles(Vec