use std::{cell::RefCell, fmt::Write as _};
use cssparser::{CowRcStr, Delimiter, ParseError, Parser, ParserInput, Token};
use less_allocator::{Allocator, Box, Vec};
use crate::{
error::ParserError,
printer::{Printer, PrinterError},
rules::variable::{Variable, VariableValue},
traits::{Parse, ToCss},
values::{token::TokenList, variable::VariableReference},
};
#[derive(Debug)]
enum TokenOrVariable<'i> {
Token(Token<'i>),
Variable(VariableReference<'i>),
}
impl<'i> ToCss<'i> for TokenOrVariable<'i> {
fn to_css<W>(&self, dest: &mut Printer<'_, 'i, W>) -> Result<(), PrinterError<'i>>
where
W: std::fmt::Write,
{
match self {
TokenOrVariable::Token(token) => <Token as cssparser::ToCss>::to_css(token, dest)?,
TokenOrVariable::Variable(variable_reference) => {
todo!()
}
};
Ok(())
}
}
#[derive(Debug)]
struct UnparsedProperty<'i> {
property_id: PropertyId<'i>,
tokens: TokenList<'i>,
raw_string: &'i str,
}
impl<'i> UnparsedProperty<'i> {
fn parse<'t>(
allocator: &'i less_allocator::Allocator,
property_id: PropertyId<'i>,
input: &mut cssparser::Parser<'i, 't>,
) -> Result<Self, cssparser::ParseError<'i, crate::error::ParserError<'i>>> {
let start_position = input.position();
let tokens = TokenList::parse_until_before(allocator, input, Delimiter::Semicolon)?;
dbg!(input.slice_from(start_position));
Ok(UnparsedProperty {
property_id,
tokens,
raw_string: input.slice_from(start_position),
})
}
}
impl<'i> ToCss<'i> for UnparsedProperty<'i> {
fn to_css<W>(&self, dest: &mut Printer<'_, 'i, W>) -> Result<(), PrinterError<'i>>
where
W: std::fmt::Write,
{
let tokens = self.tokens.evaluate(dest)?;
for token in tokens.iter() {
cssparser::ToCss::to_css(token, dest)?;
}
Ok(())
}
}
#[derive(Debug)]
pub enum PropertyDeclaration<'i> {
Unparsed(Box<'i, UnparsedProperty<'i>>),
}
pub(crate) fn resolve_and_evaluate_variable<'i, W: std::fmt::Write>(
variable: &VariableReference<'i>,
dest: &mut Printer<'_, 'i, W>,
) -> Result<&'i Variable<'i>, PrinterError<'i>> {
let resolved_variable = match dest.context.resolve_variable(variable) {
Ok(v) => v,
Err(_) => {
return Err(PrinterError::LessVariableUndefined(variable.clone()));
}
};
if resolved_variable.evaluating.get() {
return Err(PrinterError::LessRecursiveVariable(variable.clone()));
}
resolved_variable.evaluating.set(true);
let variable_parsed = matches!(*resolved_variable.value.borrow(), VariableValue::Parsed(_));
if variable_parsed {
return Ok(resolved_variable);
}
let tokens =
TokenList::parse_raw_until_before(dest.allocator, resolved_variable.raw, Delimiter::Semicolon)
.map_err(PrinterError::LessVariableParseError)?;
*resolved_variable.value.borrow_mut() = VariableValue::Parsed(tokens);
Ok(resolved_variable)
}
impl<'i> ToCss<'i> for PropertyDeclaration<'i> {
fn to_css<W: std::fmt::Write>(
&self,
dest: &mut Printer<'_, 'i, W>,
) -> Result<(), PrinterError<'i>> {
match self {
PropertyDeclaration::Unparsed(unparsed) => {
unparsed.property_id.to_css(dest)?;
dest.write_str(": ")?;
unparsed.to_css(dest)?;
dest.write_char(';')?;
dest.write_newline()?;
}
}
Ok(())
}
}
#[derive(Debug)]
struct LessVariableReference<'i> {
property_id: PropertyId<'i>,
variable: VariableReference<'i>,
}
impl<'i> LessVariableReference<'i> {
fn parse<'t>(
allocator: &'i less_allocator::Allocator,
property_id: &PropertyId<'i>,
input: &mut cssparser::Parser<'i, 't>,
) -> Result<Self, cssparser::ParseError<'i, crate::error::ParserError<'i>>> {
Ok(Self {
property_id: property_id.clone(),
variable: VariableReference::parse(allocator, input)?,
})
}
}
#[derive(Debug, Clone)]
pub struct PropertyId<'i>(CowRcStr<'i>);
impl<'i> ToCss<'i> for PropertyId<'i> {
fn to_css<W: std::fmt::Write>(
&self,
dest: &mut Printer<'_, 'i, W>,
) -> Result<(), PrinterError<'i>> {
dest.write_str(&self.0)?;
Ok(())
}
}
impl<'i> From<CowRcStr<'i>> for PropertyId<'i> {
fn from(name: CowRcStr<'i>) -> Self {
PropertyId(name)
}
}
impl<'i> PropertyDeclaration<'i> {
pub fn parse<'t>(
allocator: &'i less_allocator::Allocator,
property_id: &PropertyId<'i>,
input: &mut cssparser::Parser<'i, 't>,
) -> Result<Self, cssparser::ParseError<'i, crate::error::ParserError<'i>>> {
Ok(Self::Unparsed(Box(allocator.alloc(
UnparsedProperty::parse(allocator, property_id.clone(), input)?,
))))
}
}