use crate::{Cells, CellsFormatter};
use self::HorizontalAlignment::*;
use std::{cmp::min, fmt::*};
#[derive(Clone, Copy, Default)]
pub struct CellStyle {
pub(crate) align_h: Option<HorizontalAlignment>,
}
impl CellStyle {
pub fn new() -> Self {
Self::default()
}
pub fn or(self, style: CellStyle) -> CellStyle {
CellStyle {
align_h: self.align_h.or(style.align_h),
}
}
pub fn align_h(self, value: HorizontalAlignment) -> Self {
CellStyle {
align_h: Some(value),
}
}
}
#[derive(Clone, Copy)]
pub enum HorizontalAlignment {
Left,
Center,
Right,
}
pub trait RawCell {
fn fmt(&self, s: &mut String);
fn style(&self) -> CellStyle {
CellStyle::default()
}
fn style_for_body(&self) -> CellStyle {
CellStyle::default()
}
}
impl RawCell for () {
fn fmt(&self, _: &mut String) {}
}
impl<T: ?Sized + RawCell> RawCell for &T {
fn fmt(&self, s: &mut String) {
T::fmt(*self, s)
}
fn style(&self) -> CellStyle {
T::style(*self)
}
fn style_for_body(&self) -> CellStyle {
T::style_for_body(*self)
}
}
impl<T: ?Sized + RawCell> RawCell for &mut T {
fn fmt(&self, s: &mut String) {
T::fmt(*self, s)
}
fn style(&self) -> CellStyle {
T::style(*self)
}
fn style_for_body(&self) -> CellStyle {
T::style_for_body(*self)
}
}
impl<T: RawCell> RawCell for Option<T> {
fn fmt(&self, s: &mut String) {
if let Some(value) = self {
value.fmt(s);
}
}
fn style(&self) -> CellStyle {
if let Some(value) = self {
value.style()
} else {
CellStyle::default()
}
}
fn style_for_body(&self) -> CellStyle {
if let Some(value) = self {
value.style_for_body()
} else {
CellStyle::default()
}
}
}
impl<T: RawCell, E: RawCell> RawCell for std::result::Result<T, E> {
fn fmt(&self, s: &mut String) {
match self {
Ok(value) => value.fmt(s),
Err(value) => value.fmt(s),
}
}
fn style(&self) -> CellStyle {
match self {
Ok(value) => value.style(),
Err(value) => value.style(),
}
}
fn style_for_body(&self) -> CellStyle {
match self {
Ok(value) => value.style_for_body(),
Err(value) => value.style_for_body(),
}
}
}
struct DisplayCellSource<T: Display>(T);
impl<T: Display> RawCell for DisplayCellSource<T> {
fn fmt(&self, s: &mut String) {
write!(s, "{}", self.0).unwrap()
}
}
pub fn cell(value: impl Display) -> Cell<impl RawCell> {
Cell::new(DisplayCellSource(value))
}
struct FmtFnCellSource<F>(F);
impl<F: Fn(&mut String) -> Result> RawCell for FmtFnCellSource<F> {
fn fmt(&self, s: &mut String) {
(self.0)(s).unwrap()
}
}
pub fn cell_by<F: Fn(&mut String) -> Result>(f: F) -> Cell<impl RawCell> {
Cell::new(FmtFnCellSource(f))
}
#[macro_export]
macro_rules! cell {
($ ( $ arg : tt ) *) => { {
use std::fmt::Write;
$crate::cell_by(move |f| write!(f, $($arg)*))
}
};
}
pub struct Cell<T> {
source: T,
style: CellStyle,
}
impl<T: RawCell> RawCell for Cell<T> {
fn fmt(&self, s: &mut String) {
self.source.fmt(s)
}
fn style(&self) -> CellStyle {
self.style
}
}
impl<T: RawCell> Cells for Cell<T> {
fn fmt(f: &mut CellsFormatter<Self>) {
f.content_cell(|s| s);
}
}
impl<T: RawCell> Cell<T> {
pub fn new(source: T) -> Self {
let style = source.style();
Cell { source, style }
}
pub fn left(self) -> Self {
self.with_align_h(Left)
}
pub fn right(self) -> Self {
self.with_align_h(Right)
}
pub fn center(self) -> Self {
self.with_align_h(Center)
}
pub fn baseline(self, baseline: &str) -> impl Cells {
let mut value = String::new();
self.fmt(&mut value);
BaselineAlignedCell::new(value, baseline)
}
pub fn with_base_style(self, style: CellStyle) -> Self {
Cell {
source: self.source,
style: self.style.or(style),
}
}
fn with_align_h(self, align_h: HorizontalAlignment) -> Self {
Cell {
source: self.source,
style: CellStyle {
align_h: Some(align_h),
},
}
}
}
impl Cell<String> {
pub fn empty() -> Self {
Self::new(String::new())
}
}
macro_rules! impl_cell_source {
($t:ty, $align:expr ) => {
impl RawCell for $t {
fn fmt(&self, s: &mut String) {
write!(s, "{}", self).unwrap()
}
fn style_for_body(&self) -> CellStyle {
CellStyle {
align_h: Some($align),
}
}
}
impl Cells for $t {
fn fmt(f: &mut CellsFormatter<Self>) {
f.content_cell(|x| x);
}
}
};
}
impl_cell_source!(u8, Right);
impl_cell_source!(i8, Right);
impl_cell_source!(u16, Right);
impl_cell_source!(i16, Right);
impl_cell_source!(u32, Right);
impl_cell_source!(i32, Right);
impl_cell_source!(u64, Right);
impl_cell_source!(i64, Right);
impl_cell_source!(u128, Right);
impl_cell_source!(i128, Right);
impl_cell_source!(isize, Right);
impl_cell_source!(usize, Right);
impl_cell_source!(String, Left);
impl_cell_source!(str, Left);
impl_cell_source!(char, Center);
impl_cell_source!(bool, Center);
struct BaselineAlignedCell {
value: String,
baseline_offset: usize,
}
impl BaselineAlignedCell {
fn new(value: String, baseline: &str) -> Self {
let baseline_offset = value.find(baseline).unwrap_or(value.len());
Self {
value,
baseline_offset,
}
}
fn left(&self) -> &str {
&self.value[..self.baseline_offset]
}
fn right(&self) -> &str {
&self.value[self.baseline_offset..]
}
}
impl Cells for BaselineAlignedCell {
fn fmt(f: &mut CellsFormatter<Self>) {
f.content(|this| cell(this.left()).right());
f.content(|this| cell(this.right()).left());
}
}
impl Cells for f32 {
fn fmt(f: &mut CellsFormatter<Self>) {
f.content(|this| cell(this).baseline("."))
}
}
impl Cells for f64 {
fn fmt(f: &mut CellsFormatter<Self>) {
f.content(|this| cell(this).baseline("."))
}
}
#[macro_export]
macro_rules! cells_f {
($($t:tt)*) => {
$crate::cells_f(format!($($t)*))
};
}
pub fn cells_f(value: impl Display) -> impl Cells {
ExpCells::new(value.to_string())
}
struct ExpCells {
value: String,
offset_dot: usize,
offset_e: usize,
}
impl ExpCells {
fn new(value: String) -> Self {
let offset_e = value.rfind(['e', 'E']).unwrap_or(value.len());
let offset_dot = min(value.find('.').unwrap_or(value.len()), offset_e);
Self {
value,
offset_dot,
offset_e,
}
}
}
impl Cells for ExpCells {
fn fmt(f: &mut CellsFormatter<Self>) {
f.stretch()
.content(|x| cell(&x.value[..x.offset_dot]).right());
f.content(|x| {
if x.offset_dot < x.offset_e {
&x.value[x.offset_dot..x.offset_e]
} else {
""
}
});
f.content(|x| {
Cell::new(if x.offset_e < x.value.len() {
Ok(cell!(" {} ", &x.value[x.offset_e..x.offset_e + 1]))
} else {
Err(())
})
});
f.content(|x| cell(&x.value[min(x.offset_e + 1, x.value.len())..]).right());
}
}