use crate::colors::{Color, RgbColor};
use crate::mode::ColorMode;
use std::fmt;
use std::fmt::{Alignment, Debug, Display};
use std::ops::{Add, AddAssign};
pub trait StyledText: Sized {
fn s<T: Display>(self, s: T) -> Self;
fn r<T: Display>(self, s: T) -> Self {
self.reset().s(s)
}
fn reset(self) -> Self;
fn repeat<T: Display>(self, s: T, n: usize) -> Self;
fn plural<T: Display>(self, s: T, n: usize) -> Self;
fn align_left<T: Display>(self, s: T, width: usize) -> Self;
fn align_right<T: Display>(self, s: T, width: usize) -> Self;
fn align_center<T: Display>(self, s: T, width: usize) -> Self;
fn pad<T: Display>(self, ch: char, padding: usize, s: T) -> Self;
fn pad_left<T: Display>(self, ch: char, s: T, width: usize) -> Self;
fn pad_right<T: Display>(self, ch: char, s: T, width: usize) -> Self;
fn pad_center<T: Display>(self, ch: char, s: T, width: usize) -> Self;
fn fill(self, ch: char, width: usize) -> Self;
fn choose<T: Display>(self, condition: bool, when_true: T, when_false: T) -> Self;
fn indent<T: Display>(self, indent: usize, s: T) -> Self;
fn bold(self) -> Self;
fn italic(self) -> Self;
fn underline(self) -> Self;
fn black(self) -> Self;
fn bright_black(self) -> Self;
fn red(self) -> Self;
fn bright_red(self) -> Self;
fn green(self) -> Self;
fn bright_green(self) -> Self;
fn yellow(self) -> Self;
fn bright_yellow(self) -> Self;
fn blue(self) -> Self;
fn bright_blue(self) -> Self;
fn magenta(self) -> Self;
fn bright_magenta(self) -> Self;
fn cyan(self) -> Self;
fn bright_cyan(self) -> Self;
fn white(self) -> Self;
fn bright_white(self) -> Self;
fn bg_black(self) -> Self;
fn bg_bright_black(self) -> Self;
fn bg_red(self) -> Self;
fn bg_bright_red(self) -> Self;
fn bg_green(self) -> Self;
fn bg_bright_green(self) -> Self;
fn bg_yellow(self) -> Self;
fn bg_bright_yellow(self) -> Self;
fn bg_blue(self) -> Self;
fn bg_bright_blue(self) -> Self;
fn bg_magenta(self) -> Self;
fn bg_bright_magenta(self) -> Self;
fn bg_cyan(self) -> Self;
fn bg_bright_cyan(self) -> Self;
fn bg_white(self) -> Self;
fn bg_bright_white(self) -> Self;
fn color(self, c: Color) -> Self;
fn bright_color(self, c: Color) -> Self;
fn bg_color(self, c: Color) -> Self;
fn bg_bright_color(self, c: Color) -> Self;
fn color_8(self, c: u8) -> Self;
fn bright_color_8(self, c: u8) -> Self;
fn bg_color_8(self, c: u8) -> Self;
fn bg_bright_color_8(self, c: u8) -> Self;
fn color_256(self, c: u8) -> Self;
fn bg_color_256(self, c: u8) -> Self;
fn color_rgb(self, c: RgbColor) -> Self;
fn bg_color_rgb(self, c: RgbColor) -> Self;
}
#[derive(Debug, Clone)]
enum Sequence {
Char(char),
Control(String),
}
impl Display for Sequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Sequence::Char(ch) => write!(f, "{}", ch),
Sequence::Control(s) => write!(f, "{}", s),
}
}
}
impl Sequence {
fn is_char(&self) -> bool {
matches!(self, Sequence::Char(_))
}
}
#[derive(Debug, Default, Clone)]
struct Content(Vec<Sequence>);
impl Display for Content {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for sequence in &self.0 {
write!(f, "{}", sequence)?;
}
Ok(())
}
}
impl Content {
fn chars(&self) -> impl Iterator<Item = char> + '_ {
self.0.iter().filter_map(|sequence| match sequence {
Sequence::Char(c) => Some(*c),
_ => None,
})
}
fn write(&self, f: &mut fmt::Formatter<'_>, max: usize) -> fmt::Result {
let mut count = 0;
for sequence in &self.0 {
write!(f, "{}", sequence)?;
if sequence.is_char() {
count += 1;
}
if count == max {
break;
}
}
Ok(())
}
fn chr(&mut self, ch: char) {
self.0.push(Sequence::Char(ch));
}
fn str(&mut self, s: &str) {
for ch in s.chars() {
self.0.push(Sequence::Char(ch));
}
}
fn str_n(&mut self, s: &str, n: usize) {
for ch in s.chars().take(n) {
self.0.push(Sequence::Char(ch));
}
}
fn ctrl(&mut self, s: impl ToString) {
self.0.push(Sequence::Control(s.to_string()));
}
fn append(&mut self, s: &Content) {
for sequence in &s.0 {
self.0.push(sequence.clone());
}
}
fn pad(&mut self, ch: char, n: usize) {
for _ in 0..n {
self.chr(ch);
}
}
fn fill(&mut self, ch: char, length: usize) {
for _ in 0..length.saturating_sub(self.chars().count()) {
self.0.push(Sequence::Char(ch));
}
}
}
#[derive(Debug, Clone)]
pub struct Text {
cm: ColorMode,
content: Content,
}
impl Display for Text {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(width) = f.width() {
let fill = width.saturating_sub(self.content.chars().count());
let ch = f.fill().to_string();
if let Some(align) = f.align() {
match align {
Alignment::Left => {
self.content.write(f, width)?;
write!(f, "{}", ch.repeat(fill))?;
}
Alignment::Right => {
write!(f, "{}", ch.repeat(fill))?;
self.content.write(f, width)?;
}
Alignment::Center => {
let left_fill = fill / 2;
write!(f, "{}", ch.repeat(left_fill))?;
self.content.write(f, width)?;
write!(f, "{}", ch.repeat(fill - left_fill))?;
}
}
} else {
self.content.write(f, width)?;
write!(f, "{}", ch.repeat(fill))?;
}
} else {
write!(f, "{}", self.content)?;
}
Ok(())
}
}
impl Default for Text {
fn default() -> Self {
Self::new(ColorMode::default())
}
}
impl From<ColorMode> for Text {
fn from(cm: ColorMode) -> Self {
Self::new(cm)
}
}
impl Text {
pub fn new(cm: ColorMode) -> Self {
Self { cm, content: Default::default() }
}
pub fn auto() -> Self {
Self {
cm: ColorMode::default(),
content: Default::default(),
}
}
pub fn on() -> Self {
Self {
cm: ColorMode::On,
content: Default::default(),
}
}
pub fn off() -> Self {
Self {
cm: ColorMode::Off,
content: Default::default(),
}
}
pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
self.content.chars()
}
pub fn count(&self) -> usize {
self.chars().count()
}
}
impl StyledText for Text {
fn s<T: Display>(mut self, s: T) -> Self {
self.content.str(&format!("{}", s));
self
}
fn reset(mut self) -> Self {
self.content.ctrl(self.cm.reset());
self
}
fn repeat<T: Display>(self, s: T, n: usize) -> Self {
self.s(s.to_string().repeat(n))
}
fn plural<T: Display>(self, s: T, n: usize) -> Self {
if n == 1 { self.s(s) } else { self.s(format!("{}s", s)) }
}
fn align_left<T: Display>(self, s: T, width: usize) -> Self {
self.pad_right(' ', s, width)
}
fn align_right<T: Display>(self, s: T, width: usize) -> Self {
self.pad_left(' ', s, width)
}
fn align_center<T: Display>(self, s: T, width: usize) -> Self {
self.pad_center(' ', s, width)
}
fn pad<T: Display>(mut self, ch: char, padding: usize, s: T) -> Self {
self.content.pad(ch, padding);
self.content.str(&format!("{}", s));
self
}
fn pad_left<T: Display>(mut self, ch: char, s: T, width: usize) -> Self {
let buffer = format!("{}", s);
self.content.pad(ch, width.saturating_sub(buffer.chars().count()));
self.content.str_n(&buffer, width);
self
}
fn pad_right<T: Display>(mut self, ch: char, s: T, width: usize) -> Self {
let buffer = format!("{}", s);
self.content.str_n(&buffer, width);
self.content.pad(ch, width.saturating_sub(buffer.chars().count()));
self
}
fn pad_center<T: Display>(mut self, ch: char, s: T, width: usize) -> Self {
let buffer = format!("{}", s);
let fill = width.saturating_sub(buffer.chars().count());
let left_fill = fill / 2;
self.content.pad(ch, left_fill);
self.content.str_n(&buffer, width);
self.content.pad(ch, fill - left_fill);
self
}
fn fill(mut self, ch: char, width: usize) -> Self {
self.content.fill(ch, width);
self
}
fn choose<T: Display>(self, condition: bool, when_true: T, when_false: T) -> Self {
self.s(if condition { when_true } else { when_false })
}
fn indent<T: Display>(self, indent: usize, s: T) -> Self {
self.pad(' ', indent, s)
}
fn bold(mut self) -> Self {
self.content.ctrl(self.cm.bold());
self
}
fn italic(mut self) -> Self {
self.content.ctrl(self.cm.italic());
self
}
fn underline(mut self) -> Self {
self.content.ctrl(self.cm.underline());
self
}
fn black(mut self) -> Self {
self.content.ctrl(self.cm.black(false));
self
}
fn bright_black(mut self) -> Self {
self.content.ctrl(self.cm.black(true));
self
}
fn red(mut self) -> Self {
self.content.ctrl(self.cm.red(false));
self
}
fn bright_red(mut self) -> Self {
self.content.ctrl(self.cm.red(true));
self
}
fn green(mut self) -> Self {
self.content.ctrl(self.cm.green(false));
self
}
fn bright_green(mut self) -> Self {
self.content.ctrl(self.cm.green(true));
self
}
fn yellow(mut self) -> Self {
self.content.ctrl(self.cm.yellow(false));
self
}
fn bright_yellow(mut self) -> Self {
self.content.ctrl(self.cm.yellow(true));
self
}
fn blue(mut self) -> Self {
self.content.ctrl(self.cm.blue(false));
self
}
fn bright_blue(mut self) -> Self {
self.content.ctrl(self.cm.blue(true));
self
}
fn magenta(mut self) -> Self {
self.content.ctrl(self.cm.magenta(false));
self
}
fn bright_magenta(mut self) -> Self {
self.content.ctrl(self.cm.magenta(true));
self
}
fn cyan(mut self) -> Self {
self.content.ctrl(self.cm.cyan(false));
self
}
fn bright_cyan(mut self) -> Self {
self.content.ctrl(self.cm.cyan(true));
self
}
fn white(mut self) -> Self {
self.content.ctrl(self.cm.white(false));
self
}
fn bright_white(mut self) -> Self {
self.content.ctrl(self.cm.white(true));
self
}
fn bg_black(mut self) -> Self {
self.content.ctrl(self.cm.bg_black(false));
self
}
fn bg_bright_black(mut self) -> Self {
self.content.ctrl(self.cm.bg_black(true));
self
}
fn bg_red(mut self) -> Self {
self.content.ctrl(self.cm.bg_red(false));
self
}
fn bg_bright_red(mut self) -> Self {
self.content.ctrl(self.cm.bg_red(true));
self
}
fn bg_green(mut self) -> Self {
self.content.ctrl(self.cm.bg_green(false));
self
}
fn bg_bright_green(mut self) -> Self {
self.content.ctrl(self.cm.bg_green(true));
self
}
fn bg_yellow(mut self) -> Self {
self.content.ctrl(self.cm.bg_yellow(false));
self
}
fn bg_bright_yellow(mut self) -> Self {
self.content.ctrl(self.cm.bg_yellow(true));
self
}
fn bg_blue(mut self) -> Self {
self.content.ctrl(self.cm.bg_blue(false));
self
}
fn bg_bright_blue(mut self) -> Self {
self.content.ctrl(self.cm.bg_blue(true));
self
}
fn bg_magenta(mut self) -> Self {
self.content.ctrl(self.cm.bg_magenta(false));
self
}
fn bg_bright_magenta(mut self) -> Self {
self.content.ctrl(self.cm.bg_magenta(true));
self
}
fn bg_cyan(mut self) -> Self {
self.content.ctrl(self.cm.bg_cyan(false));
self
}
fn bg_bright_cyan(mut self) -> Self {
self.content.ctrl(self.cm.bg_cyan(true));
self
}
fn bg_white(mut self) -> Self {
self.content.ctrl(self.cm.bg_white(false));
self
}
fn bg_bright_white(mut self) -> Self {
self.content.ctrl(self.cm.bg_white(true));
self
}
fn color(mut self, c: Color) -> Self {
self.content.ctrl(self.cm.color(c, false));
self
}
fn bright_color(mut self, c: Color) -> Self {
self.content.ctrl(self.cm.color(c, true));
self
}
fn bg_color(mut self, c: Color) -> Self {
self.content.ctrl(self.cm.bg_color(c, false));
self
}
fn bg_bright_color(mut self, c: Color) -> Self {
self.content.ctrl(self.cm.bg_color(c, true));
self
}
fn color_8(mut self, c: u8) -> Self {
self.content.ctrl(self.cm.color_8(c, false));
self
}
fn bright_color_8(mut self, c: u8) -> Self {
self.content.ctrl(self.cm.color_8(c, true));
self
}
fn bg_color_8(mut self, c: u8) -> Self {
self.content.ctrl(self.cm.bg_color_8(c, false));
self
}
fn bg_bright_color_8(mut self, c: u8) -> Self {
self.content.ctrl(self.cm.bg_color_8(c, true));
self
}
fn color_256(mut self, c: u8) -> Self {
self.content.ctrl(self.cm.color_256(c));
self
}
fn bg_color_256(mut self, c: u8) -> Self {
self.content.ctrl(self.cm.bg_color_256(c));
self
}
fn color_rgb(mut self, c: RgbColor) -> Self {
self.content.ctrl(self.cm.color_rgb(c));
self
}
fn bg_color_rgb(mut self, c: RgbColor) -> Self {
self.content.ctrl(self.cm.bg_color_rgb(c));
self
}
}
impl AddAssign<Text> for Text {
fn add_assign(&mut self, rhs: Text) {
self.content.append(&rhs.content);
}
}
impl AddAssign<&Text> for Text {
fn add_assign(&mut self, rhs: &Text) {
self.content.append(&rhs.content);
}
}
impl AddAssign<&str> for Text {
fn add_assign(&mut self, rhs: &str) {
self.content.str(rhs);
}
}
impl AddAssign<String> for Text {
fn add_assign(&mut self, rhs: String) {
self.content.str(&rhs);
}
}
impl AddAssign<&String> for Text {
fn add_assign(&mut self, rhs: &String) {
self.content.str(rhs);
}
}
impl AddAssign<char> for Text {
fn add_assign(&mut self, rhs: char) {
self.content.str(&rhs.to_string());
}
}
impl Add<Text> for Text {
type Output = Text;
fn add(mut self, rhs: Text) -> Text {
self += rhs;
self
}
}
impl Add<&Text> for Text {
type Output = Text;
fn add(mut self, rhs: &Text) -> Text {
self += rhs;
self
}
}
impl Add<&str> for Text {
type Output = Text;
fn add(mut self, rhs: &str) -> Text {
self += rhs;
self
}
}
impl Add<String> for Text {
type Output = Text;
fn add(mut self, rhs: String) -> Text {
self += rhs;
self
}
}
impl Add<&String> for Text {
type Output = Text;
fn add(mut self, rhs: &String) -> Text {
self += rhs;
self
}
}
impl Add<char> for Text {
type Output = Text;
fn add(mut self, rhs: char) -> Text {
self += rhs;
self
}
}
impl Add<Text> for &str {
type Output = Text;
fn add(self, rhs: Text) -> Text {
self.add(&rhs)
}
}
impl Add<&Text> for &str {
type Output = Text;
fn add(self, rhs: &Text) -> Text {
let mut content = Content::default();
content.str(self);
content.append(&rhs.content);
Text { cm: rhs.cm, content }
}
}
impl Add<Text> for String {
type Output = Text;
fn add(self, rhs: Text) -> Text {
self.add(&rhs)
}
}
impl Add<&Text> for String {
type Output = Text;
fn add(self, rhs: &Text) -> Text {
let mut content = Content::default();
content.str(&self);
content.append(&rhs.content);
Text { cm: rhs.cm, content }
}
}
impl Add<Text> for &String {
type Output = Text;
fn add(self, rhs: Text) -> Text {
self.add(&rhs)
}
}
impl Add<&Text> for &String {
type Output = Text;
fn add(self, rhs: &Text) -> Text {
self.as_str().add(rhs)
}
}
impl Add<Text> for char {
type Output = Text;
fn add(self, rhs: Text) -> Text {
self.add(&rhs)
}
}
impl Add<&Text> for char {
type Output = Text;
fn add(self, rhs: &Text) -> Text {
let mut content = Content::default();
content.chr(self);
content.append(&rhs.content);
Text { cm: rhs.cm, content }
}
}
pub fn auto() -> Text {
Text::auto()
}
pub fn always() -> Text {
Text::on()
}
pub fn never() -> Text {
Text::off()
}