#![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, // this crate is too new to deny todo!() clippy::todo, // 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, // for now, panic() is an acceptable solution clippy::panic, // for now, some functions require a lot of lines // future refactoring should make functions small and make // this lint less annoying clippy::too_many_lines, // 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 )] #![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; 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}; mod args; mod atrule; 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; pub type SassResult = Result; #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct Token { pos: Pos, pub kind: TokenKind, } 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 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, Eq, PartialEq)] pub struct StyleSheet(Vec); #[derive(Clone, Debug, Eq, PartialEq)] 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), } /// Represents a single rule set. Rule sets can contain other rule sets /// /// ```scss /// a { /// color: blue; /// b { /// color: red; /// } /// } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] 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), /// A collection of styles, from a mixin or function // Styles(Vec