use std::ops::{Deref, DerefMut};
use cssparser::{CowRcStr, ParseError};
use less_allocator::{Allocator, Vec};
use crate::{error::ParserError, rules::variable::Variable, values::variable::VariableReference};
#[derive(Debug)]
pub(crate) struct Context<'i> {
variables: Vec<'i, &'i Variable<'i>>,
}
#[derive(Debug)]
pub(crate) struct Contexts<'i>(Vec<'i, Context<'i>>);
impl<'i> Contexts<'i> {
pub(crate) fn resolve_variable(
&self,
variable: &VariableReference,
) -> Result<&'i Variable<'i>, ()> {
match self.0.iter().rev().find_map(|context| {
context
.variables
.iter()
.rev()
.find(|v| v.name == variable.0)
}) {
Some(variable) => Ok(variable),
None => Err(()),
}
}
}
impl<'a, 'i> Deref for Contexts<'i> {
type Target = Vec<'i, Context<'i>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a, 'i> DerefMut for Contexts<'i> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'i> Contexts<'i> {
fn new(allocator: &'i Allocator) -> Self {
Self(Vec::new_in(allocator))
}
}
#[derive(Debug)]
pub enum PrinterError<'i> {
LessVariableParseError(ParseError<'i, ParserError<'i>>),
LessRecursiveVariable(VariableReference<'i>),
LessVariableUndefined(VariableReference<'i>),
LessPropertyUndefined(CowRcStr<'i>),
Error(std::fmt::Error),
}
impl From<std::fmt::Error> for PrinterError<'_> {
fn from(value: std::fmt::Error) -> Self {
PrinterError::Error(value)
}
}
pub struct Printer<'a, 'i, W: std::fmt::Write> {
pub(crate) allocator: &'i Allocator,
dest: &'a mut W,
pub(crate) context: Contexts<'i>,
pub(crate) col: u32,
pub(crate) line: u32,
}
impl<'a, 'i, W: std::fmt::Write> Printer<'a, 'i, W> {
pub fn new(allocator: &'i Allocator, dest: &'a mut W) -> Self {
Self {
allocator,
dest,
context: Contexts::new(allocator),
col: 0,
line: 0,
}
}
pub fn with_context<F: FnOnce(&mut Printer<'a, 'i, W>) -> Result<(), PrinterError<'i>>>(
&mut self,
variables: Vec<&Variable>,
f: F,
) -> Result<(), PrinterError<'i>> {
let variables = unsafe {
#[expect(clippy::missing_transmute_annotations)]
std::mem::transmute(variables)
};
let context = Context { variables };
self.context.push(context);
f(self)?;
self.context.pop();
Ok(())
}
pub fn write_str(&mut self, s: &str) -> Result<(), PrinterError<'i>> {
debug_assert!(!s.contains('\n'), "String contains newline: '{}'", s);
self.col += s.len() as u32;
self.dest.write_str(s)?;
Ok(())
}
pub fn write_char(&mut self, c: char) -> Result<(), PrinterError<'i>> {
if c == '\n' {
self.write_newline()?;
return Ok(());
}
self.col += 1;
self.dest.write_char(c)?;
Ok(())
}
pub fn write_newline(&mut self) -> Result<(), PrinterError<'i>> {
self.col += 1;
self.line = 0;
self.dest.write_char('\n')?;
Ok(())
}
}
impl<W: std::fmt::Write> std::fmt::Write for Printer<'_, '_, W> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
debug_assert!(!s.contains('\n'), "String contains newline: {}", s);
self.col += s.len() as u32;
self.dest.write_str(s)
}
}