use std::iter::Sum;
use std::ops::{Add, Deref, DerefMut};
use nu_ansi_term::{AnsiString as ANSIString, AnsiStrings as ANSIStrings, Style};
use unicode_width::UnicodeWidthStr;
#[derive(PartialEq, Debug, Clone, Default)]
pub struct TextCell {
pub contents: TextCellContents,
pub width: DisplayWidth,
}
impl Deref for TextCell {
type Target = TextCellContents;
fn deref(&self) -> &Self::Target {
&self.contents
}
}
impl TextCell {
pub fn paint(style: Style, text: String) -> Self {
let width = DisplayWidth::from(&*text);
Self {
contents: vec![style.paint(text)].into(),
width,
}
}
pub fn paint_str(style: Style, text: &'static str) -> Self {
let width = DisplayWidth::from(text);
Self {
contents: vec![style.paint(text)].into(),
width,
}
}
pub fn blank(style: Style) -> Self {
Self {
contents: vec![style.paint("-")].into(),
width: DisplayWidth::from(1),
}
}
pub fn add_spaces(&mut self, count: usize) {
(*self.width) += count;
let spaces: String = " ".repeat(count);
self.contents.0.push(Style::default().paint(spaces));
}
pub fn push(&mut self, string: ANSIString<'static>, extra_width: usize) {
self.contents.0.push(string);
(*self.width) += extra_width;
}
pub fn append(&mut self, other: Self) {
(*self.width) += *other.width;
self.contents.0.extend(other.contents.0);
}
}
#[derive(PartialEq, Debug, Clone, Default)]
pub struct TextCellContents(Vec<ANSIString<'static>>);
impl From<Vec<ANSIString<'static>>> for TextCellContents {
fn from(strings: Vec<ANSIString<'static>>) -> Self {
Self(strings)
}
}
impl Deref for TextCellContents {
type Target = [ANSIString<'static>];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TextCellContents {
pub fn strings(&self) -> ANSIStrings<'_> {
ANSIStrings(&self.0)
}
pub fn width(&self) -> DisplayWidth {
self.0
.iter()
.map(|anstr| DisplayWidth::from(anstr.as_str()))
.sum()
}
pub fn promote(self) -> TextCell {
TextCell {
width: self.width(),
contents: self,
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
pub struct DisplayWidth(usize);
impl<'a> From<&'a str> for DisplayWidth {
fn from(input: &'a str) -> Self {
Self(UnicodeWidthStr::width(input))
}
}
impl From<usize> for DisplayWidth {
fn from(width: usize) -> Self {
Self(width)
}
}
impl Deref for DisplayWidth {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for DisplayWidth {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Add for DisplayWidth {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Add<usize> for DisplayWidth {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs)
}
}
impl Sum for DisplayWidth {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Self>,
{
iter.fold(Self(0), Add::add)
}
}
#[cfg(test)]
mod width_unit_test {
use super::DisplayWidth;
#[test]
fn empty_string() {
let cell = DisplayWidth::from("");
assert_eq!(*cell, 0);
}
#[test]
fn test_string() {
let cell = DisplayWidth::from("Diss Playwidth");
assert_eq!(*cell, 14);
}
#[test]
fn addition() {
let cell_one = DisplayWidth::from("/usr/bin/");
let cell_two = DisplayWidth::from("drinking");
assert_eq!(*(cell_one + cell_two), 17);
}
#[test]
fn addition_usize() {
let cell = DisplayWidth::from("/usr/bin/");
assert_eq!(*(cell + 8), 17);
}
}