use crate::gen::block::Block; use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::gen::Opt; use crate::syntax::namespace::Namespace; use crate::syntax::Types; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile<'a> { pub header: bool, pub opt: &'a Opt, pub types: &'a Types<'a>, pub include: Includes<'a>, pub builtin: Builtins<'a>, content: RefCell>, } #[derive(Default)] pub struct Content<'a> { bytes: String, namespace: &'a Namespace, blocks: Vec>, section_pending: bool, blocks_pending: usize, } #[derive(Copy, Clone, PartialEq, Debug)] enum BlockBoundary<'a> { Begin(Block<'a>), End(Block<'a>), } impl<'a> OutFile<'a> { pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self { OutFile { header, opt, types, include: Includes::new(), builtin: Builtins::new(), content: RefCell::new(Content::new()), } } // Write a blank line if the preceding section had any contents. pub fn next_section(&mut self) { self.content.get_mut().next_section(); } pub fn begin_block(&mut self, block: Block<'a>) { self.content.get_mut().begin_block(block); } pub fn end_block(&mut self, block: Block<'a>) { self.content.get_mut().end_block(block); } pub fn set_namespace(&mut self, namespace: &'a Namespace) { self.content.get_mut().set_namespace(namespace); } pub fn write_fmt(&self, args: Arguments) { let content = &mut *self.content.borrow_mut(); Write::write_fmt(content, args).unwrap(); } pub fn content(&mut self) -> Vec { self.flush(); let include = &self.include.content.bytes; let builtin = &self.builtin.content.bytes; let content = &self.content.get_mut().bytes; let len = include.len() + builtin.len() + content.len() + 2; let mut out = String::with_capacity(len); out.push_str(include); if !out.is_empty() && !builtin.is_empty() { out.push('\n'); } out.push_str(builtin); if !out.is_empty() && !content.is_empty() { out.push('\n'); } out.push_str(content); if out.is_empty() { out.push_str("// empty\n"); } out.into_bytes() } fn flush(&mut self) { self.include.content.flush(); self.builtin.content.flush(); self.content.get_mut().flush(); } } impl<'a> Write for Content<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { self.write(s); Ok(()) } } impl<'a> PartialEq for Content<'a> { fn eq(&self, _other: &Self) -> bool { true } } impl<'a> Content<'a> { fn new() -> Self { Content::default() } pub fn next_section(&mut self) { self.section_pending = true; } pub fn begin_block(&mut self, block: Block<'a>) { self.push_block_boundary(BlockBoundary::Begin(block)); } pub fn end_block(&mut self, block: Block<'a>) { self.push_block_boundary(BlockBoundary::End(block)); } pub fn set_namespace(&mut self, namespace: &'a Namespace) { for name in self.namespace.iter().rev() { self.end_block(Block::UserDefinedNamespace(name)); } for name in namespace { self.begin_block(Block::UserDefinedNamespace(name)); } self.namespace = namespace; } pub fn write_fmt(&mut self, args: Arguments) { Write::write_fmt(self, args).unwrap(); } fn write(&mut self, b: &str) { if !b.is_empty() { if self.blocks_pending > 0 { self.flush_blocks(); } if self.section_pending && !self.bytes.is_empty() { self.bytes.push('\n'); } self.bytes.push_str(b); self.section_pending = false; self.blocks_pending = 0; } } fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) { if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() { self.blocks.pop(); self.blocks_pending -= 1; } else { self.blocks.push(boundary); self.blocks_pending += 1; } } fn flush(&mut self) { self.set_namespace(Default::default()); if self.blocks_pending > 0 { self.flush_blocks(); } } fn flush_blocks(&mut self) { self.section_pending = !self.bytes.is_empty(); let mut read = self.blocks.len() - self.blocks_pending; let mut write = read; while read < self.blocks.len() { match self.blocks[read] { BlockBoundary::Begin(begin_block) => { if self.section_pending { self.bytes.push('\n'); self.section_pending = false; } Block::write_begin(begin_block, &mut self.bytes); self.blocks[write] = BlockBoundary::Begin(begin_block); write += 1; } BlockBoundary::End(end_block) => { write = write.checked_sub(1).unwrap(); let begin_block = self.blocks[write]; assert_eq!(begin_block, BlockBoundary::Begin(end_block)); Block::write_end(end_block, &mut self.bytes); self.section_pending = true; } } read += 1; } self.blocks.truncate(write); } } impl<'a> BlockBoundary<'a> { fn rev(self) -> BlockBoundary<'a> { match self { BlockBoundary::Begin(block) => BlockBoundary::End(block), BlockBoundary::End(block) => BlockBoundary::Begin(block), } } }