diff --git a/src/common.rs b/src/common.rs index a8bae90..a9827e3 100644 --- a/src/common.rs +++ b/src/common.rs @@ -454,6 +454,7 @@ impl ListSeparator { } } + // Used in currently unimplemented builtin function #[allow(dead_code)] pub fn name(self) -> &'static str { match self { diff --git a/src/css.rs b/src/css.rs index 5ad13eb..bdc5955 100644 --- a/src/css.rs +++ b/src/css.rs @@ -1,11 +1,12 @@ //! # Convert from SCSS AST to CSS +use std::fmt; +use std::io::Write; +use std::sync::atomic::Ordering; + use crate::atrule::AtRule; use crate::error::SassResult; use crate::lexer::IS_UTF8; use crate::{RuleSet, Selector, Stmt, Style, StyleSheet}; -use std::fmt; -use std::io::Write; -use std::sync::atomic::Ordering; #[derive(Debug, Clone)] enum Toplevel { @@ -24,8 +25,8 @@ enum BlockEntry { impl fmt::Display for BlockEntry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - BlockEntry::Style(s) => writeln!(f, " {}", s), - BlockEntry::MultilineComment(s) => writeln!(f, " /*{}*/", s), + BlockEntry::Style(s) => writeln!(f, "{}", s), + BlockEntry::MultilineComment(s) => writeln!(f, "/*{}*/", s), } } } @@ -112,8 +113,8 @@ impl Css { } else { self.blocks.push(Toplevel::Newline); } + self.blocks.extend(v); } - self.blocks.extend(v); } self } @@ -133,7 +134,7 @@ impl Css { has_written = true; writeln!(buf, "{}{} {{", padding, selector)?; for style in styles { - write!(buf, "{}{}", padding, style)?; + write!(buf, "{} {}", padding, style)?; } writeln!(buf, "{}}}", padding)?; } @@ -148,7 +149,7 @@ impl Css { } else { writeln!(buf, "{}@{} {} {{", padding, u.name, u.params)?; } - Css::from_stylesheet(StyleSheet::from_stmts(u.body.clone())) + Css::from_stylesheet(StyleSheet::from_stmts(u.body)) .pretty_print(buf, nesting + 1)?; writeln!(buf, "{}}}", padding)?; } diff --git a/src/error.rs b/src/error.rs index a62a894..9b813b9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,10 @@ -use crate::common::Pos; use std::error::Error; use std::fmt::{self, Display}; use std::io; use std::string::FromUtf8Error; +use crate::common::Pos; + pub type SassResult = Result; #[derive(Debug)] diff --git a/src/imports.rs b/src/imports.rs index 3cae2e6..f178728 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -1,8 +1,9 @@ +use std::ffi::OsStr; +use std::path::Path; + use crate::common::Scope; use crate::error::SassResult; use crate::{Stmt, StyleSheet}; -use std::ffi::OsStr; -use std::path::Path; pub(crate) fn import>(path: P) -> SassResult<(Vec, Scope)> { let mut rules: Vec = Vec::new(); diff --git a/src/lexer.rs b/src/lexer.rs index 92ef571..ad0b36e 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -150,11 +150,6 @@ impl<'a> Iterator for Lexer<'a> { } } -#[allow(dead_code)] -fn is_whitespace(c: char) -> bool { - c == ' ' || c == '\n' || c == '\r' || c == FORM_FEED -} - impl<'a> Lexer<'a> { pub fn new(buf: &'a str) -> Lexer<'a> { Lexer { @@ -193,20 +188,6 @@ impl<'a> Lexer<'a> { } } - #[allow(dead_code)] - fn devour_whitespace(&mut self) -> bool { - let mut found_whitespace = false; - while let Some(c) = self.buf.peek() { - if !is_whitespace(*c) { - return found_whitespace; - } - found_whitespace = true; - self.buf.next(); - self.pos.next_char(); - } - found_whitespace - } - fn lex_at_rule(&mut self) -> TokenKind { self.buf.next(); self.pos.next_char(); diff --git a/src/lib.rs b/src/lib.rs index 191b016..b217829 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,6 @@ 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; diff --git a/src/main.rs b/src/main.rs index 1e03cde..ca03d1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ -use clap::{App, Arg}; use std::io::{stdout, BufWriter}; +use clap::{App, Arg}; + use grass::StyleSheet; fn main() { diff --git a/src/selector.rs b/src/selector.rs index f77fcc6..b797fc0 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1,3 +1,7 @@ +use std::fmt::{self, Display, Write}; +use std::iter::Peekable; +use std::string::ToString; + use crate::common::{Scope, Symbol, Whitespace}; use crate::error::SassResult; use crate::lexer::Lexer; @@ -6,9 +10,6 @@ use crate::utils::{ parse_quoted_string, IsWhitespace, }; use crate::{Token, TokenKind}; -use std::fmt::{self, Display, Write}; -use std::iter::Peekable; -use std::string::ToString; #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct Selector(pub Vec); diff --git a/src/style.rs b/src/style.rs index 443a973..e92b1da 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,11 +1,12 @@ +use std::fmt::{self, Display}; +use std::iter::Peekable; + use crate::common::{Pos, QuoteKind, Scope, Symbol}; use crate::error::SassResult; use crate::selector::Selector; use crate::utils::{devour_whitespace, parse_interpolation, parse_quoted_string}; use crate::value::Value; use crate::{Expr, Token, TokenKind}; -use std::fmt::{self, Display}; -use std::iter::Peekable; /// A style: `color: red` #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/src/utils.rs b/src/utils.rs index db9a6f3..50d2d6f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,10 +1,11 @@ +use std::iter::{Iterator, Peekable}; + use crate::common::{Keyword, QuoteKind, Symbol}; use crate::error::SassResult; use crate::lexer::Lexer; use crate::selector::Selector; use crate::value::Value; use crate::{Scope, Token, TokenKind}; -use std::iter::{Iterator, Peekable}; pub(crate) trait IsWhitespace { fn is_whitespace(&self) -> bool; diff --git a/src/value/mod.rs b/src/value/mod.rs index 252810b..988b282 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_variables)] use std::fmt::{self, Display, Write}; use std::iter::Iterator; diff --git a/src/value/ops.rs b/src/value/ops.rs index c4c0b24..44972f6 100644 --- a/src/value/ops.rs +++ b/src/value/ops.rs @@ -22,7 +22,16 @@ impl Add for Value { _ => Value::Ident(format!("{}", other), QuoteKind::None), }, Self::Dimension(num, unit) => match other { - Self::Dimension(num2, unit2) => Value::Dimension(num + num2, unit), + Self::Dimension(num2, unit2) => { + if !unit.comparable(&unit2) { + return Err(format!("Incompatible units {} and {}.", unit2, unit).into()); + } + if unit == unit2 { + Value::Dimension(num + num2, unit) + } else { + todo!("unit conversions") + } + } Self::Ident(s, q) => { let quotes = match q { QuoteKind::Double | QuoteKind::Single => QuoteKind::Double, @@ -99,7 +108,16 @@ impl Sub for Value { Ok(match self { Self::Null => todo!(), Self::Dimension(num, unit) => match other { - Self::Dimension(num2, unit2) => Value::Dimension(num - num2, unit), + Self::Dimension(num2, unit2) => { + if !unit.comparable(&unit2) { + return Err(format!("Incompatible units {} and {}.", unit2, unit).into()); + } + if unit == unit2 { + Value::Dimension(num - num2, unit) + } else { + todo!("unit conversions") + } + } _ => todo!(), }, // Self::List(..) => todo!(), @@ -181,7 +199,22 @@ impl Mul for Value { Ok(match self { Self::Null => todo!(), Self::Dimension(num, unit) => match other { - Self::Dimension(num2, unit2) => Value::Dimension(num * num2, unit), + Self::Dimension(num2, unit2) => { + if unit2 != Unit::None && unit != Unit::None { + return Err(format!( + "{}{}*{} isn't a valid CSS value.", + num * num2, + unit, + unit2 + ) + .into()); + } + if unit == unit2 { + Value::Dimension(num * num2, unit) + } else { + todo!("unit conversions") + } + } _ => { return Err( format!("Undefined operation \"{}{} * {}\".", num, unit, other).into(), diff --git a/src/value/parse.rs b/src/value/parse.rs index 47a258b..209cf40 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -75,7 +75,7 @@ impl Value { super_selector: &Selector, ) -> SassResult { let left = Self::_from_tokens(toks, scope, super_selector)?; - let whitespace = devour_whitespace_or_comment(toks); + devour_whitespace_or_comment(toks); let next = match toks.peek() { Some(x) => x, None => return Ok(left), diff --git a/tests/macros.rs b/tests/macros.rs index 43b31b4..035d7d4 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -1,6 +1,6 @@ #![cfg(test)] -#[allow(unused_macros)] +#[macro_export] macro_rules! test { ($func:ident, $input:literal) => { #[test]