use ast::{TransformedNode, AstLocation, Comment};
use ast::transform::{Transform, TransformResult, TransformContext, RenderContext};
use ast::block_statements::*;
use ast::expression::*;
use scope::{ScopeData, ScopeValue};
use std::rc::Rc;
use std::cell::RefCell;
use std::io::Write;
use errors::Result;
#[derive(Debug, Clone)]
pub struct Block {
pub nodes: Vec<BlockStatement>,
pub variant: BlockType,
pub location: AstLocation,
pub condition: Option<Box<ExpressionNode>>,
pub block_comments: Vec<Comment>,
pub inline_comment: Option<Comment>,
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum BlockType {
Show,
Hide,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PlainBlock {
pub variant: BlockType,
pub nodes: Vec<TransformedNode>,
pub block_comments: Vec<Comment>,
pub inline_comment: Option<Comment>,
}
trait NodeList {
fn push_statement(&mut self, value: TransformedNode);
}
impl NodeList for Vec<TransformedNode> {
fn push_statement(&mut self, value: TransformedNode) {
if let TransformedNode::ConditionStmt(_) = value {
self.push(value);
} else if let Some(index) = self.iter().position(|other| *other == value) {
self[index] = value;
} else {
self.push(value);
}
}
}
impl Transform for Block {
fn transform(&self, ctx: TransformContext)
-> Result<Option<TransformedNode>> {
if let Some(ref condition) = self.condition {
let condition_result = condition
.transform(ctx.clone())?
.and_then(|transformed_node| Some(transformed_node.return_value()));
if let Some(ScopeValue::Bool(b)) = condition_result {
if !b {
return Ok(None)
}
}
}
let block_ctx = TransformContext {
scope: Rc::new(RefCell::new(
ScopeData::new(Some(ctx.scope.clone()))
)),
path: ctx.path.clone(),
};
let mut t_statements: Vec<TransformedNode> = Vec::new();
for statement in &self.nodes {
if let Some(t_statement) = statement.transform(block_ctx.clone())? {
match t_statement {
TransformedNode::ExpandedNodes(resolved_stmts) => {
for stmt in resolved_stmts {
t_statements.push_statement(stmt);
}
}
_ => {
t_statements.push_statement(t_statement);
}
}
}
}
Ok(Some(TransformedNode::Block(
PlainBlock {
nodes: t_statements,
variant: self.variant,
block_comments: self.block_comments.clone(),
inline_comment: self.inline_comment.clone()
}
)))
}
fn location(&self) -> AstLocation {
unimplemented!()
}
}
impl TransformResult for PlainBlock {
fn render(&self, ctx: RenderContext, buf: &mut Write) -> Result<()> {
if ctx.config.pretty { buf.write_all(ctx.config.line_ending)?; }
for comment in &self.block_comments {
comment.render(ctx, buf)?;
}
buf.write_all(match self.variant {
BlockType::Show => b"Show",
BlockType::Hide => b"Hide"
})?;
self.inline_comment.render(ctx, buf)?;
for n in &self.nodes {
n.render(ctx.increase_indent(), buf)?;
}
Ok(())
}
}