use std::fmt::{self, Write};
use crate::grid::{Dir, Pos};
use crate::lattice::Graph;
pub struct Printer<'a> {
graph: &'a Graph,
max_x: i64,
height: i64,
buffer: String,
}
impl fmt::Display for Graph {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
Printer::new(self).print();
Ok(())
}
}
impl<'a> Printer<'a> {
pub fn new(graph: &'a Graph) -> Self {
let max_x = graph.nodes.keys().map(|id| id.pos.x).max().unwrap_or(0);
let height = graph.positioner.height as i64;
let buffer_size = Self::calculate_buffer_size(max_x, height);
Self {
graph,
max_x,
height,
buffer: String::with_capacity(buffer_size),
}
}
fn calculate_buffer_size(max_x: i64, height: i64) -> usize {
let width = (max_x + 2) as usize; let header_size = 5 + width * 4 + 1; let row_size = 3 + width * 4 + 1; let diagonal_size = 2 + width * 4 + 1;
header_size + (height as usize + 1) * (row_size + diagonal_size)
}
pub fn print(&mut self) {
self.print_header();
self.buffer.push('\n');
self.print_diagonal_parity(-1);
self.buffer.push('\n');
for y in 0..self.height {
self.print_data_and_horizontal(y);
self.buffer.push('\n');
self.print_diagonal_parity(y);
self.buffer.push('\n');
}
print!("{}", self.buffer);
}
fn print_header(&mut self) {
write!(self.buffer, " ").unwrap();
for x in 0..=self.max_x {
write!(self.buffer, "{:<4}", x).unwrap();
}
}
fn print_diagonal_parity(&mut self, y: i64) {
write!(self.buffer, " ").unwrap();
for x in -1..=self.max_x {
let has_left = !(x == self.max_x && y == self.height - 1 || x == -1 && y == -1)
&& self
.graph
.has_parity_node_along_dir(Pos::new(x, y + 1), Dir::UR);
let has_right = !(x == self.max_x && y == -1 || x == -1 && y == self.height - 1)
&& self
.graph
.has_parity_node_along_dir(Pos::new(x, y), Dir::DR);
let symbol = if has_left && has_right {
" X "
} else if has_left {
" / "
} else if has_right {
" \\ "
} else {
" "
};
self.buffer.push_str(symbol);
}
}
fn print_data_and_horizontal(&mut self, y: i64) {
write!(self.buffer, "{:<3}", y).unwrap();
let left_edge_pos = Pos::new(self.max_x, y);
if self.graph.has_parity_node_along_dir(left_edge_pos, Dir::R) {
self.buffer.push('-');
} else {
self.buffer.push(' ');
}
self.buffer.push(' ');
for x in 0..=self.max_x {
let pos = Pos::new(x, y);
if self.graph.has_data_node(pos) {
self.buffer.push('O');
} else {
self.buffer.push(' ');
}
if x < self.max_x {
if self.graph.has_parity_node_along_dir(pos, Dir::R) {
self.buffer.push_str(" - ");
} else {
self.buffer.push_str(" ");
}
}
}
let right_edge_pos = Pos::new(0, y);
if self.graph.has_parity_node_along_dir(right_edge_pos, Dir::L) {
self.buffer.push_str(" -");
} else {
self.buffer.push_str(" ");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parity::StrandType;
use bytes::Bytes;
#[test]
fn test_print_graph() {
let mut graph = Graph::new(4, 16);
let b = || -> Bytes { Bytes::new() };
graph.add_data_node(Pos::new(0, 0), b());
graph.add_data_node(Pos::new(1, 1), b());
graph.add_data_node(Pos::new(2, 2), b());
graph.add_data_node(Pos::new(3, 3), b());
graph.add_data_node(Pos::new(0, 3), b());
graph.add_data_node(Pos::new(3, 0), b());
graph.add_parity_node(Pos::new(0, 0), b(), StrandType::Right);
graph.add_parity_node(Pos::new(0, 0), b(), StrandType::Left);
graph.add_parity_node(Pos::new(3, 0), b(), StrandType::Right);
graph.add_parity_node(Pos::new(3, 0), b(), StrandType::Left);
graph.add_parity_node(Pos::new(0, 3), b(), StrandType::Right);
graph.add_parity_node(Pos::new(0, 3), b(), StrandType::Left);
graph.add_parity_node(Pos::new(3, 3), b(), StrandType::Right);
graph.add_parity_node(Pos::new(3, 3), b(), StrandType::Left);
graph.add_parity_node(Pos::new(1, 1), b(), StrandType::Horizontal);
graph.add_parity_node(Pos::new(1, 1), b(), StrandType::Left);
graph.add_parity_node(Pos::new(1, 0), b(), StrandType::Right);
graph.add_parity_node(Pos::new(3, 1), b(), StrandType::Horizontal);
graph.add_parity_node(Pos::new(2, 2), b(), StrandType::Horizontal);
let mut printer = Printer::new(&graph);
printer.print();
let expected_output = r#" 0 1 2 3
\ X /
0 O O
\ \ X \
1 - O - -
2 O -
/ / /
3 O O
/ X \
"#;
assert_eq!(printer.buffer, expected_output);
}
}