use std::iter::Peekable; use super::{eat_stmts, AtRule, AtRuleKind}; use crate::common::Symbol; use crate::error::SassResult; use crate::scope::Scope; use crate::selector::Selector; use crate::utils::{ devour_whitespace_or_comment, read_until_closing_curly_brace, read_until_open_curly_brace, }; use crate::value::Value; use crate::{Stmt, Token, TokenKind}; #[derive(Debug, Clone)] pub(crate) struct If { pub branches: Vec, pub else_: Vec, } #[derive(Debug, Clone)] pub(crate) struct Branch { pub cond: Vec, pub toks: Vec, } impl Branch { pub fn new(cond: Vec, toks: Vec) -> Branch { Branch { cond, toks } } } impl If { pub fn from_tokens>(toks: &mut Peekable) -> SassResult { let mut branches = Vec::new(); let init_cond = read_until_open_curly_brace(toks); toks.next(); devour_whitespace_or_comment(toks); let mut init_toks = read_until_closing_curly_brace(toks); init_toks.push(toks.next().unwrap()); devour_whitespace_or_comment(toks); branches.push(Branch::new(init_cond, init_toks)); let mut else_ = Vec::new(); loop { if let Some(tok) = toks.peek() { if tok.kind == TokenKind::AtRule(AtRuleKind::Else) { toks.next(); devour_whitespace_or_comment(toks); if let Some(tok) = toks.next() { devour_whitespace_or_comment(toks); if tok.kind.to_string().to_ascii_lowercase() == "if" { let cond = read_until_open_curly_brace(toks); toks.next(); devour_whitespace_or_comment(toks); let mut toks_ = read_until_closing_curly_brace(toks); toks_.push(toks.next().unwrap()); devour_whitespace_or_comment(toks); branches.push(Branch::new(cond, toks_)) } else if tok.is_symbol(Symbol::OpenCurlyBrace) { else_ = read_until_closing_curly_brace(toks); toks.next(); break; } else { return Err("expected \"{\".".into()); } } } else { break; } } else { break; } } devour_whitespace_or_comment(toks); Ok(If { branches, else_ }) } pub fn eval(self, scope: &mut Scope, super_selector: &Selector) -> SassResult> { let mut stmts = Vec::new(); let mut toks = Vec::new(); let mut found_true = false; for branch in self.branches { if Value::from_tokens( &mut branch.cond.into_iter().peekable(), scope, super_selector, )? .is_true()? { toks = branch.toks; found_true = true; break; } } if !found_true { toks = self.else_; } for stmt in eat_stmts(&mut toks.into_iter().peekable(), scope, super_selector)? { match stmt { Stmt::AtRule(AtRule::If(i)) => stmts.extend(i.eval(scope, super_selector)?), Stmt::RuleSet(r) if r.selector.is_empty() => stmts.extend(r.rules), v => stmts.push(v), } } Ok(stmts) } }