#![no_std]
use ufmt::{uDebug, uDisplay, uWrite, uwrite, uwriteln, Formatter};
#[allow(non_camel_case_types)]
pub struct uDataTable<'a, T: Copy + Default + uDebug + uDisplay, const N: usize, const M: usize> {
headers: [&'a str; M],
data: [T; N],
length: usize,
}
#[allow(dead_code)]
impl<'a, T: Copy + Default + uDebug + uDisplay, const N: usize, const M: usize>
uDataTable<'a, T, N, M>
{
pub fn new(headers: [&'a str; M]) -> Self {
Self {
headers,
data: [T::default(); N],
length: 0,
}
}
pub fn get(&self, index: usize) -> Result<&T, uDataTableError> {
if index < self.length {
Result::Ok(&self.data[index])
} else {
Result::Err(uDataTableError::RowIndexOutOfBounds)
}
}
pub fn append(&mut self, row: T) -> Result<&T, uDataTableError> {
if self.length < N {
self.data[self.length] = row;
self.length += 1;
Result::Ok(&self.data[self.length - 1])
} else {
Result::Err(uDataTableError::CannotGrowTable)
}
}
pub fn erase(&mut self) {
self.length = 0;
for i in 0..N {
self.data[i] = T::default();
}
}
pub fn length(&self) -> usize {
self.length
}
pub fn headers(&self) -> &[&'a str; M] {
&self.headers
}
#[cfg(any(feature = "plot", doc))]
pub fn plot<W>(&self, f: &mut W, value: fn(&T) -> i32)
where
W: uWrite + ?Sized,
{
let mut min = i32::MAX;
let mut max = i32::MIN;
for row in self.data.iter() {
let value = value(row);
if value < min {
min = value;
}
if value > max {
max = value;
}
}
let min_digits = Self::count_digits(min);
let max_digits = Self::count_digits(max);
let digits = if min_digits > max_digits {
min_digits
} else {
max_digits
};
let scale = 1.0 / (max - min) as f32;
const MAX_HEIGHT: i32 = 23;
let display_height = if (max - min) as i32 > MAX_HEIGHT {
MAX_HEIGHT
} else {
(max - min) as i32
};
for h in (0..display_height + 1).rev() {
if h == (display_height as f32 * (0 - min) as f32 / (max - min) as f32) as i32 {
Self::write_n_spaces(digits - 1, f);
uwrite!(f, "0 |").ok();
} else if h == display_height {
Self::write_n_spaces(digits - max_digits, f);
uwrite!(f, "{} |", max).ok();
} else if h == 0 {
Self::write_n_spaces(digits - min_digits, f);
uwrite!(f, "{} |", min).ok();
} else {
Self::write_n_spaces(digits, f);
uwrite!(f, " |").ok();
}
for r in 0..self.length() {
if let Result::Ok(row) = self.get(r) {
let value = value(row);
let scaled_value =
((value - min) as f32 * scale * display_height as f32) as i32;
if scaled_value == h {
uwrite!(f, "*").ok();
} else if scaled_value > h {
uwrite!(f, ".").ok();
} else {
uwrite!(f, " ").ok();
}
}
}
uwriteln!(f, "").ok();
}
}
fn count_digits(value: i32) -> u32 {
let mut n = value;
let mut count = 0;
if n < 0 {
n = -n;
count += 1; }
loop {
count += 1;
n /= 10;
if n == 0 {
break;
}
}
count
}
#[cfg(feature = "plot")]
fn write_n_spaces<W>(n: u32, f: &mut W)
where
W: uWrite + ?Sized,
{
for _ in 0..n {
uwrite!(f, " ").ok();
}
}
}
impl<'a, T: Copy + Default + uDebug + uDisplay, const N: usize, const M: usize> uDebug
for uDataTable<'a, T, N, M>
{
fn fmt<W>(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
uwrite!(f, "uDataTable<[\"")?;
for i in 0..M {
uwrite!(f, "{}", self.headers[i])?;
if i < M - 1 {
uwrite!(f, "\", \"")?;
}
}
uwrite!(f, "\"], length: {}>", self.length)?;
Result::Ok(())
}
}
impl<'a, T: Copy + Default + uDebug + uDisplay, const N: usize, const M: usize> uDisplay
for uDataTable<'a, T, N, M>
{
fn fmt<W>(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
for i in 0..M {
uwrite!(f, "\"{}\"", self.headers[i])?;
if i < M - 1 {
uwrite!(f, ",")?;
}
}
uwrite!(f, "\n")?;
for i in 0..self.length {
uwriteln!(f, "{}", self.data[i])?;
}
Ok(())
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
pub enum uDataTableError {
RowIndexOutOfBounds,
CannotGrowTable,
}
impl uDebug for uDataTableError {
fn fmt<W>(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
match self {
uDataTableError::RowIndexOutOfBounds => uwrite!(f, "RowIndexOutOfBounds"),
uDataTableError::CannotGrowTable => uwrite!(f, "CannotGrowTable"),
}
}
}
impl uDisplay for uDataTableError {
fn fmt<W>(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
uDebug::fmt(self, f)
}
}