use std::io::Write;
use super::scope::Scope;
const INDENT_STEP: usize = 2;
pub struct DotWriter<'w> {
writer: &'w mut dyn Write,
indent: usize,
next_id: usize,
digraph: bool,
pretty_print: bool,
}
impl<'w, W: Write> From<&'w mut W> for DotWriter<'w> {
fn from(writer: &'w mut W) -> Self {
Self {
writer,
indent: 0,
next_id: 0,
digraph: false,
pretty_print: true,
}
}
}
impl<'w> DotWriter<'w> {
pub fn set_pretty_print(&mut self, pretty_print: bool) {
self.pretty_print = pretty_print;
}
pub fn graph(&mut self) -> Scope<'_, 'w> {
self.digraph = false;
Scope::new(self, b"graph")
}
pub fn digraph(&mut self) -> Scope<'_, 'w> {
self.digraph = true;
Scope::new(self, b"digraph")
}
pub fn write_string<F: Fn(&mut DotWriter)>(builder: F) -> String {
let mut bytes = Vec::new();
let mut writer = DotWriter::from(&mut bytes);
writer.set_pretty_print(false);
(builder)(&mut writer);
String::from_utf8(bytes).unwrap()
}
}
impl<'w> DotWriter<'w> {
pub(crate) fn write(&mut self, bytes: &[u8]) {
if self.pretty_print {
self.writer.write_all(bytes).unwrap();
} else {
for b in bytes.iter().filter(|b| !b.is_ascii_whitespace()) {
self.writer.write_all(&[*b]).unwrap();
}
}
}
pub(crate) fn write_with_whitespace(&mut self, bytes: &[u8]) {
self.writer.write_all(bytes).unwrap();
}
pub(crate) fn write_quoted(&mut self, bytes: &[u8]) {
self.writer.write_all(b"\"").unwrap();
self.write_with_whitespace(bytes);
self.writer.write_all(b"\"").unwrap();
}
pub(crate) fn write_indent(&mut self) {
for _ in 0..self.indent {
self.writer.write_all(b" ").unwrap();
}
}
pub(crate) fn write_edge_operator(&mut self) {
match self.digraph {
true => self.write(b" -> "),
false => self.write(b" -- "),
}
}
pub(crate) fn indent(&mut self) {
if self.pretty_print {
self.indent += INDENT_STEP;
}
}
pub(crate) fn unindent(&mut self) {
if self.pretty_print {
self.indent -= INDENT_STEP;
}
}
pub(crate) fn next_id(&mut self) -> usize {
let next_id = self.next_id;
self.next_id += 1;
next_id
}
}
pub(crate) struct Statement<'d, 'w> {
line: Line<'d, 'w>,
}
impl<'d, 'w> Statement<'d, 'w> {
pub(crate) fn new(writer: &'d mut DotWriter<'w>) -> Self {
Self {
line: Line::new(writer),
}
}
}
impl<'d, 'w> std::ops::DerefMut for Statement<'d, 'w> {
fn deref_mut(&mut self) -> &mut DotWriter<'w> {
&mut *self.line
}
}
impl<'d, 'w> std::ops::Deref for Statement<'d, 'w> {
type Target = DotWriter<'w>;
fn deref(&self) -> &DotWriter<'w> {
&*self.line
}
}
impl<'d, 'w> Drop for Statement<'d, 'w> {
fn drop(&mut self) {
self.line.write(b";");
}
}
pub(crate) struct Line<'d, 'w> {
writer: &'d mut DotWriter<'w>,
}
impl<'d, 'w> Line<'d, 'w> {
pub(crate) fn new(writer: &'d mut DotWriter<'w>) -> Self {
writer.write_indent();
Self { writer }
}
}
impl<'d, 'w> std::ops::DerefMut for Line<'d, 'w> {
fn deref_mut(&mut self) -> &mut DotWriter<'w> {
self.writer
}
}
impl<'d, 'w> std::ops::Deref for Line<'d, 'w> {
type Target = DotWriter<'w>;
fn deref(&self) -> &DotWriter<'w> {
self.writer
}
}
impl<'a, 'b: 'a> Drop for Line<'a, 'b> {
fn drop(&mut self) {
if self.writer.pretty_print {
self.writer.write(b"\n");
}
}
}