From 058772edb4a58821c76124812512c84dd7d084df Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Mon, 20 Jan 2020 11:00:01 -0500 Subject: [PATCH] Refactor into library and add argument parsing --- Cargo.toml | 15 +- src/lib.rs | 1217 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1286 +++------------------------------------------------ 3 files changed, 1293 insertions(+), 1225 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 44ed8ed..92ef707 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,22 @@ repository = "https://github.com/connorskees/grass" authors = ["ConnorSkees <39542938+ConnorSkees@users.noreply.github.com>"] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "grass" +path = "src/main.rs" +required-features = ["commandline"] + +[lib] +name = "grass" +path = "src/lib.rs" +edition = "2018" [dependencies] +clap = { version = "2.33.0", optional = true } + +[features] +default = ["commandline"] +commandline = ["clap"] [dev-dependencies] # test imports diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cf2c0e3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,1217 @@ +#![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 +)] +#![feature(track_caller)] +// todo! handle erroring on styles at the toplevel +use std::fmt::{self, Display}; +use std::fs; +use std::io::{self, Write}; +use std::iter::{Iterator, Peekable}; +use std::path::Path; + +use crate::common::{AtRuleKind, Keyword, Op, Pos, Printer, Scope, Symbol, Whitespace}; +use crate::css::Css; +use crate::error::SassError; +use crate::format::PrettyPrinter; +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::units::Unit; +use crate::utils::{devour_whitespace, eat_variable_value, IsWhitespace}; + +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; + +pub type SassResult = Result; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub 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 + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TokenKind { + Ident(String), + Symbol(Symbol), + String(String), + AtRule(AtRuleKind), + Keyword(Keyword), + Number(String), + Unit(Unit), + Whitespace(Whitespace), + Variable(String), + Attribute(Attribute), + Op(Op), + MultilineComment(String), + Interpolation, +} + +impl Display for TokenKind { + 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::String(s) => write!(f, "\"{}\"", s), + TokenKind::AtRule(s) => write!(f, "{}", s), + TokenKind::Op(s) => write!(f, "{}", s), + TokenKind::Unit(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 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 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