use super::Matrix;
use super::order::Order;
use crate::index::Index;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt;
mod whitespace {
pub(super) const SPACE: &str = " ";
pub(super) const INDENT: &str = " ";
pub(super) const NEWLINE: &str = "\n";
}
mod matrix {
pub(super) const DELIMITER_LEFT: &str = "[";
pub(super) const DELIMITER_RIGHT: &str = "]";
}
mod row {
pub(super) const INDEX_GAP: &str = " ";
pub(super) const DELIMITER_LEFT: &str = "[ ";
pub(super) const DELIMITER_RIGHT: &str = " ]";
pub(super) const DELIMITER_PADDING: &str = " ";
pub(super) const SEPARATOR: &str = "";
pub(super) const SEPARATOR_PADDING: &str = "";
}
mod element {
pub(super) const INDEX_GAP: &str = " ";
pub(super) const SEPARATOR: &str = " ";
pub(super) const SEPARATOR_PADDING: &str = " ";
}
impl<T, O> fmt::Debug for Matrix<T, O>
where
T: fmt::Debug,
O: Order,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(matrix::DELIMITER_RIGHT)?;
return Ok(());
}
let stride = self.stride();
let shape = self.shape();
let size = self.size();
let index_width = size.to_string().chars().count();
let mut element_width = 0;
let mut element_hight = 0;
let mut cache = Vec::with_capacity(size);
for element in &self.data {
let lines = Lines::from(format!("{element:?}"));
element_width = usize::max(element_width, lines.width);
element_hight = usize::max(element_hight, lines.height);
cache.push(lines);
}
let index_padding = whitespace::SPACE.repeat(index_width);
let element_padding = whitespace::SPACE.repeat(element_width);
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(whitespace::NEWLINE)?;
f.write_str(whitespace::INDENT)?;
f.write_str(&index_padding)?;
f.write_str(row::INDEX_GAP)?;
f.write_str(row::DELIMITER_PADDING)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR_PADDING)?;
}
f.write_index(col, index_width)?;
f.write_str(element::INDEX_GAP)?;
f.write_str(&element_padding)?;
}
f.write_str(row::DELIMITER_PADDING)?;
f.write_str(row::SEPARATOR_PADDING)?;
f.write_str(whitespace::NEWLINE)?;
for row in 0..shape.nrows {
f.write_str(whitespace::INDENT)?;
f.write_index(row, index_width)?;
f.write_str(row::INDEX_GAP)?;
f.write_str(row::DELIMITER_LEFT)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR)?;
}
let index = Index::new(row, col).to_linear::<O>(stride);
f.write_index(index, index_width)?;
f.write_str(element::INDEX_GAP)?;
match cache[index].next_line() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_RIGHT)?;
f.write_str(row::SEPARATOR)?;
f.write_str(whitespace::NEWLINE)?;
for _ in 1..element_hight {
f.write_str(whitespace::INDENT)?;
f.write_str(&index_padding)?;
f.write_str(row::INDEX_GAP)?;
f.write_str(row::DELIMITER_PADDING)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR_PADDING)?;
}
let index = Index::new(row, col).to_linear::<O>(stride);
f.write_str(&index_padding)?;
f.write_str(element::INDEX_GAP)?;
match cache[index].next_line() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_PADDING)?;
f.write_str(row::SEPARATOR_PADDING)?;
f.write_str(whitespace::NEWLINE)?;
}
}
f.write_str(matrix::DELIMITER_RIGHT)
}
}
impl<T, O> fmt::Display for Matrix<T, O>
where
T: fmt::Display,
O: Order,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(matrix::DELIMITER_RIGHT)?;
return Ok(());
}
let stride = self.stride();
let shape = self.shape();
let size = self.size();
let mut element_width = 0;
let mut element_hight = 0;
let mut cache = Vec::with_capacity(size);
for element in &self.data {
let lines = Lines::from(format!("{element}"));
element_width = usize::max(element_width, lines.width);
element_hight = usize::max(element_hight, lines.height);
cache.push(lines);
}
let element_padding = whitespace::SPACE.repeat(element_width);
f.write_str(matrix::DELIMITER_LEFT)?;
f.write_str(whitespace::NEWLINE)?;
for row in 0..shape.nrows {
f.write_str(whitespace::INDENT)?;
f.write_str(row::DELIMITER_LEFT)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR)?;
}
let index = Index::new(row, col).to_linear::<O>(stride);
match cache[index].next_line() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_RIGHT)?;
f.write_str(row::SEPARATOR)?;
f.write_str(whitespace::NEWLINE)?;
for _ in 1..element_hight {
f.write_str(whitespace::INDENT)?;
f.write_str(row::DELIMITER_PADDING)?;
for col in 0..shape.ncols {
if col != 0 {
f.write_str(element::SEPARATOR_PADDING)?;
}
let index = Index::new(row, col).to_linear::<O>(stride);
match cache[index].next_line() {
None if element_width > 0 => f.write_str(&element_padding)?,
Some(line) => f.write_element_line(line, element_width)?,
_ => (),
}
}
f.write_str(row::DELIMITER_PADDING)?;
f.write_str(row::SEPARATOR_PADDING)?;
f.write_str(whitespace::NEWLINE)?;
}
}
f.write_str(matrix::DELIMITER_RIGHT)
}
}
#[derive(Debug)]
struct Lines {
width: usize,
height: usize,
index: usize,
string: Box<str>,
}
impl Lines {
fn next_line(&mut self) -> Option<&str> {
let len = self.string.len();
if self.index == len {
return None;
}
let slice = &self.string[self.index..];
match slice.find('\n') {
None => {
self.index = len;
Some(slice)
}
Some(index) => {
self.index += index + 1;
let slice = &slice[..index];
slice.strip_suffix('\r').or(Some(slice))
}
}
}
}
impl From<String> for Lines {
fn from(value: String) -> Self {
let mut width = 0;
let mut height = 0;
for line in value.lines() {
let line_width = line.chars().count();
width = usize::max(width, line_width);
height += 1;
}
let index = 0;
let string = value.into_boxed_str();
Self {
width,
height,
index,
string,
}
}
}
trait Write {
fn write_index(&mut self, index: usize, width: usize) -> fmt::Result;
fn write_element_line(&mut self, line: &str, width: usize) -> fmt::Result;
}
impl<T> Write for T
where
T: fmt::Write,
{
#[cfg(not(feature = "pretty-debug"))]
fn write_index(&mut self, index: usize, width: usize) -> fmt::Result {
write!(self, "{index:>width$}")
}
#[cfg(feature = "pretty-debug")]
fn write_index(&mut self, index: usize, width: usize) -> fmt::Result {
let plain = format!("{index:>width$}");
let style = owo_colors::Style::new().green().dimmed();
let styled = style.style(plain);
write!(self, "{styled}")
}
fn write_element_line(&mut self, line: &str, width: usize) -> fmt::Result {
write!(self, "{line:<width$}")
}
}