use std::iter::Sum;
use std::ops::{Add, Deref, DerefMut};
use ansi_term::{Style, ANSIString, ANSIStrings};
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) {
use std::iter::repeat;
(*self.width) += count;
let spaces: String = repeat(' ').take(count).collect();
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))
.sum()
}
pub fn promote(self) -> TextCell {
TextCell {
width: self.width(),
contents: self,
}
}
}
#[derive(PartialEq, 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);
}
}