tele_codegen 0.2.0

A CSS code generator that generates CSS code from the AST generated by tele_parser
Documentation
#![warn(missing_docs)]
#![deny(unused_imports)]
#![deny(unused_variables)]

//! A CSS code generator that generates CSS code from the AST generated by tele_parser

use std::error::Error;
use std::fmt::Write;
use tele_parser::{
  AstType, AtRuleNode, DeclarationNode, DimensionNode, FunctionNode, IdentNode, NumberNode,
  OperatorNode, PercentageNode, RawNode, RuleSetNode, StatementNode, StringNode, StyleSheetNode,
  URLNode, Value,
};

pub trait CodeGenerator {
  fn generate(&mut self, ast: AstType) -> Result<(), Box<dyn Error>> {
    self.gen_ss_node(&*ast.borrow())
  }

  fn gen_ss_node(&mut self, ss_node: &StyleSheetNode) -> Result<(), Box<dyn Error>>;
  fn gen_rule_set_node(&mut self, rule_set_node: &RuleSetNode) -> Result<(), Box<dyn Error>>;
  fn gen_at_rule_node(&mut self, at_rule_node: &AtRuleNode) -> Result<(), Box<dyn Error>>;
  fn gen_decl_node(&mut self, at_rule_node: &DeclarationNode) -> Result<(), Box<dyn Error>>;
  fn gen_decl_value(&mut self, values: &Vec<Value>, top_level: bool) -> Result<(), Box<dyn Error>> {
    for (idx, value) in values.iter().enumerate() {
      let is_last = idx == values.len() - 1;
      match value {
        Value::Ident(node) => self.gen_ident_node(&*node.borrow_mut(), is_last, top_level)?,
        Value::Dimension(node) => {
          self.gen_dimension_node(&*node.borrow_mut(), is_last, top_level)?
        }
        Value::Number(node) => self.gen_number_node(&*node.borrow_mut(), is_last, top_level)?,
        Value::Operator(node) => self.gen_operator_node(&*node.borrow_mut(), is_last, top_level)?,
        Value::Percentage(node) => {
          self.gen_percentage_node(&*node.borrow_mut(), is_last, top_level)?
        }
        Value::String(node) => self.gen_string_node(&*node.borrow_mut(), is_last, top_level)?,
        Value::URL(node) => self.gen_url_node(&*node.borrow_mut(), is_last, top_level)?,
        Value::Function(node) => self.gen_fn_node(&*node.borrow_mut(), is_last, top_level)?,
        Value::Raw(node) => self.gen_raw_node(&*node.borrow_mut(), is_last, top_level)?,
      }
    }

    Ok(())
  }

  fn gen_ident_node(
    &mut self,
    ident_node: &IdentNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_dimension_node(
    &mut self,
    dimension_node: &DimensionNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_number_node(
    &mut self,
    number_node: &NumberNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_operator_node(
    &mut self,
    operator_node: &OperatorNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_percentage_node(
    &mut self,
    percentage_node: &PercentageNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_string_node(
    &mut self,
    string_node: &StringNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_url_node(
    &mut self,
    url_node: &URLNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_fn_node(
    &mut self,
    fn_node: &FunctionNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;
  fn gen_raw_node(
    &mut self,
    raw_node: &RawNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>>;

  fn gen_statements(&mut self, statements: &Vec<StatementNode>) -> Result<(), Box<dyn Error>> {
    for stat in statements {
      match stat {
        StatementNode::AtRule(node) => self.gen_at_rule_node(&*node.borrow_mut())?,
        StatementNode::RuleSet(node) => self.gen_rule_set_node(&*node.borrow_mut())?,
      }
    }

    Ok(())
  }
}

pub struct Codegen<'a, W: Write> {
  css: &'a mut W,
  line: usize,
  column: usize,
  indent: usize,
  indent_level: usize,
}

impl<'a, W: Write> Codegen<'a, W> {
  pub fn new(writer: &'a mut W) -> Self {
    Codegen {
      css: writer,
      line: 0,
      column: 1,
      indent: 2,
      indent_level: 0,
    }
  }

  fn new_line(&mut self) -> Result<(), Box<dyn Error>> {
    self.line += 1;
    self.css.write_char('\n')?;
    for _ in 0..self.indent_level * self.indent {
      self.css.write_char(' ')?;
    }

    Ok(())
  }

  fn indent(&mut self) {
    self.indent_level += 1;
  }

  fn de_indent(&mut self) {
    self.indent_level -= 1;
  }

  fn gen_value_padding(
    &mut self,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    if is_last {
      if last_should_padding {
        self.css.write_char(';')?;
      }
    } else {
      self.css.write_char(' ')?;
    };

    Ok(())
  }
}

impl<'a, W: Write> CodeGenerator for Codegen<'a, W> {
  fn gen_ss_node(&mut self, ss_node: &StyleSheetNode) -> Result<(), Box<dyn Error>> {
    self.gen_statements(&ss_node.statements)
  }
  fn gen_rule_set_node(&mut self, rule_set_node: &RuleSetNode) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&rule_set_node.prelude)?;
    self.css.write_str(" {")?;
    self.indent();

    for decl in &rule_set_node.declarations {
      self.new_line()?;
      self.gen_decl_node(&*decl.borrow())?;
    }

    self.de_indent();
    self.new_line()?;
    self.css.write_char('}')?;

    Ok(())
  }

  fn gen_at_rule_node(&mut self, at_rule_node: &AtRuleNode) -> Result<(), Box<dyn Error>> {
    self.css.write_char('@')?;
    self.css.write_str(&at_rule_node.name)?;
    self.css.write_char(' ')?;
    self.css.write_str(&at_rule_node.prelude)?;

    if at_rule_node.block.len() > 0 {
      self.css.write_char('{')?;
      self.indent();
      self.new_line()?;
      self.gen_statements(&at_rule_node.block)?;
      self.de_indent();
      self.new_line()?;
      self.css.write_char('}')?;
    } else {
      self.css.write_char(';')?;
    }

    Ok(())
  }

  fn gen_decl_node(&mut self, decl_node: &DeclarationNode) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&decl_node.name)?;
    self.css.write_str(": ")?;

    self.gen_decl_value(&decl_node.value, true)?;

    Ok(())
  }

  fn gen_ident_node(
    &mut self,
    ident_node: &IdentNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&ident_node.name)?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_dimension_node(
    &mut self,
    dimension_node: &DimensionNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&dimension_node.value)?;
    self.css.write_str(&dimension_node.unit)?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_number_node(
    &mut self,
    number_node: &NumberNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&number_node.value)?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_operator_node(
    &mut self,
    operator_node: &OperatorNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&operator_node.value)?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_percentage_node(
    &mut self,
    percentage_node: &PercentageNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&format!("{}%", percentage_node.value))?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_string_node(
    &mut self,
    string_node: &StringNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&string_node.value)?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_url_node(
    &mut self,
    url_node: &URLNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&format!("url({})", url_node.value))?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_fn_node(
    &mut self,
    fn_node: &FunctionNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&format!("{}(", fn_node.name,))?;
    self.gen_decl_value(&fn_node.children, false)?;
    self.css.write_char(')')?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }

  fn gen_raw_node(
    &mut self,
    raw_node: &RawNode,
    is_last: bool,
    last_should_padding: bool,
  ) -> Result<(), Box<dyn Error>> {
    self.css.write_str(&raw_node.value)?;
    self.gen_value_padding(is_last, last_should_padding)?;
    Ok(())
  }
}