#![warn( clippy::all, clippy::restriction, clippy::pedantic, clippy::nursery, // clippy::cargo )] #![deny(missing_debug_implementations)] #![allow( dead_code, clippy::pub_enum_variant_names, clippy::implicit_return, clippy::use_self, clippy::missing_docs_in_private_items, clippy::todo, clippy::dbg_macro, clippy::unreachable, clippy::wildcard_enum_match_arm, clippy::option_expect_used, clippy::panic, clippy::unused_self, clippy::too_many_lines, clippy::integer_arithmetic, clippy::missing_errors_doc )] use std::collections::HashMap; use std::fs; use std::io; use std::iter::Iterator; use std::{ fmt::{self, Display}, iter::Peekable, }; use crate::common::{Keyword, Pos, Symbol, Whitespace}; use crate::format::PrettyPrinter; use crate::lexer::Lexer; use crate::selector::Selector; use crate::units::Unit; mod color; mod common; mod error; mod format; mod lexer; mod selector; mod units; #[derive(Clone, Debug, Eq, PartialEq)] pub enum TokenKind { Ident(String), Symbol(Symbol), Function(String, Vec), AtRule(String), Keyword(Keyword), Number(String), Unit(Unit), Whitespace(Whitespace), Variable(String), Selector(Selector), Style(Vec), } #[derive(Clone, Debug, Eq, PartialEq)] pub enum StyleToken { Ident(String), Function(String, Vec), Keyword(Keyword), Symbol(Symbol), Dimension(String, Unit), } impl Display for StyleToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { StyleToken::Ident(s) => write!(f, "{}", s), StyleToken::Symbol(s) => write!(f, "{}", s), StyleToken::Function(name, args) => write!(f, "{}({})", name, args.join(", ")), StyleToken::Keyword(kw) => write!(f, "{}", kw), StyleToken::Dimension(val, unit) => write!(f, "{}{}", val, unit), } } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Token { pos: Pos, pub kind: TokenKind, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct StyleSheet { rules: Vec, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Style { property: String, value: Vec, } impl Display for Style { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}: {};", self.property, self.value .iter() .map(|x| format!("{}", x)) .collect::>() .join(" ") ) } } impl Style { fn from_tokens(raw: &[Token]) -> Result { let mut iter = raw.iter(); let property: String; loop { if let Some(tok) = iter.next() { match tok.kind { TokenKind::Whitespace(_) => continue, TokenKind::Ident(ref s) => { property = s.clone(); break; } _ => todo!(), }; } else { return Err(()); } } while let Some(tok) = iter.next() { match tok.kind { TokenKind::Whitespace(_) => continue, TokenKind::Symbol(Symbol::Colon) => break, _ => todo!("found tokens before style value"), } } let mut value = Vec::new(); while let Some(tok) = iter.next() { match tok.kind { TokenKind::Whitespace(_) | TokenKind::Symbol(Symbol::SingleQuote) | TokenKind::Symbol(Symbol::DoubleQuote) => continue, TokenKind::Ident(ref s) => value.push(StyleToken::Ident(s.clone())), TokenKind::Symbol(s) => value.push(StyleToken::Symbol(s)), TokenKind::Unit(u) => value.push(StyleToken::Ident(u.into())), TokenKind::Number(ref num) => { if let Some(t) = iter.next() { match &t.kind { &TokenKind::Unit(unit) => { value.push(StyleToken::Dimension(num.clone(), unit)) } TokenKind::Ident(ref s) => { value.push(StyleToken::Dimension(num.clone(), Unit::None)); value.push(StyleToken::Ident(s.clone())); } TokenKind::Whitespace(_) => { value.push(StyleToken::Dimension(num.clone(), Unit::None)) } _ => todo!(), } } else { value.push(StyleToken::Dimension(num.clone(), Unit::None)) } } _ => todo!("style value not ident or dimension"), } } Ok(Style { property, value }) } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum Stmt { Style(Style), RuleSet(RuleSet), } #[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, } #[derive(Clone, Debug, Eq, PartialEq)] enum Expr { Style(Style), Selector(Selector), } impl StyleSheet { #[must_use] pub fn new(input: &str) -> StyleSheet { StyleSheetParser { variables: HashMap::new(), lexer: Lexer::new(input).peekable(), rules: Vec::new(), } .parse_toplevel() } pub fn pretty_print(&self, buf: W) -> io::Result<()> { PrettyPrinter::new(buf).pretty_print(self) } pub fn pretty_print_selectors(&self, buf: W) -> io::Result<()> { PrettyPrinter::new(buf).pretty_print_preserve_super_selectors(self) } pub fn print_as_css(self, buf: &mut W) -> io::Result<()> { Css::from_stylesheet(self).pretty_print(buf) } } #[derive(Debug, Clone)] pub struct Block { selector: Selector, styles: Vec