use std::ops;
use std::ops::{Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
use typed_builder::TypedBuilder;
use crate::writer::{Color, DisplayAttribute};
#[derive(Clone, Copy, TypedBuilder)]
#[builder(field_defaults(default))]
pub struct ColoredChar {
#[builder(!default)]
ch: u8,
foreground_color: Color,
background_color: Color,
attribute: DisplayAttribute,
}
impl ColoredChar {
pub fn set_char(&mut self, ch: u8) {
self.ch = ch;
}
pub fn set_foreground_color(&mut self, color: Color) {
self.foreground_color = color;
}
pub fn set_background_color(&mut self, color: Color) {
self.background_color = color;
}
pub fn set_attribute(&mut self, attribute: DisplayAttribute) {
self.attribute = attribute;
}
pub fn get_char(&self) -> u8 {
self.ch
}
pub fn get_foreground_color(&self) -> Color {
self.foreground_color
}
pub fn get_background_color(&self) -> Color {
self.background_color
}
pub fn get_attribute(&self) -> DisplayAttribute {
self.attribute
}
}
impl PartialEq for ColoredChar {
fn eq(&self, other: &Self) -> bool {
self.get_char() == other.get_char() &&
self.get_background_color() == other.get_background_color() &&
self.get_foreground_color() == other.get_foreground_color() &&
self.get_attribute() == other.get_attribute()
}
}
impl PartialEq<u8> for ColoredChar {
fn eq(&self, other: &u8) -> bool {
self.get_char() == *other
}
}
impl From<char> for ColoredChar {
fn from(value: char) -> Self {
ColoredChar::builder().ch(value as u8).build()
}
}
impl From<u8> for ColoredChar {
fn from(value: u8) -> Self {
ColoredChar::builder().ch(value).build()
}
}
#[derive(Clone)]
pub struct ColoredString {
data: Vec<ColoredChar>,
}
impl ColoredString {
pub fn new_from_inner(data: &[ColoredChar]) -> Self {
Self {
data: data.to_vec(),
}
}
pub fn new() -> Self {
Self {
data: vec![]
}
}
pub fn new_from_str(data: &str) -> Self {
let mut colored_data = Vec::with_capacity(data.len());
for ch in data.bytes() {
colored_data.push(ColoredChar::builder().ch(ch).build());
}
Self {
data: colored_data,
}
}
pub fn new_from_bytes(data: &[u8]) -> Self {
let mut colored_data = Vec::with_capacity(data.len());
for ch in data {
colored_data.push(ColoredChar::builder().ch(*ch).build());
}
Self {
data: colored_data,
}
}
pub fn set_color_attribute(&mut self, index: usize, background_color: Option<Color>, foreground_color: Option<Color>, attribute: Option<DisplayAttribute>) {
if index >= self.len() {
panic!("Tried to set color at index {} with length of {}", index, self.len());
}
let current = self.data.get_mut(index).unwrap();
if background_color.is_some() {
current.set_background_color(background_color.unwrap());
}
if foreground_color.is_some() {
current.set_foreground_color(foreground_color.unwrap());
}
if attribute.is_some() {
current.set_attribute(attribute.unwrap());
}
}
pub fn set_char(&mut self, index: usize, ch: u8) {
if index >= self.len() {
panic!("Tried to set color at index {} with length of {}", index, self.len());
}
let current = self.data.get_mut(index).unwrap();
current.set_char(ch);
}
pub fn with_capacity(cap: usize) -> Self {
Self {
data: Vec::with_capacity(cap),
}
}
pub fn insert(&mut self, index: usize, item: ColoredChar) {
self.data.insert(index, item);
}
pub fn push(&mut self, item: ColoredChar) {
self.data.push(item);
}
pub fn pop(&mut self) -> Option<ColoredChar> {
match self.data.pop() {
None => { None }
Some(ch) => {
Some(ch)
}
}
}
pub fn remove(&mut self, index: usize) -> ColoredChar {
self.data.remove(index)
}
pub fn get(&self, index: usize) -> Option<&ColoredChar> {
match self.data.get(index) {
None => { None }
Some(ch) => {
Some(ch)
}
}
}
pub fn clear(&mut self) {
self.data.clear();
}
pub fn shrink_to_fit(&mut self) {
self.data.shrink_to_fit();
}
pub fn shrink_to(&mut self, size: usize) {
self.data.shrink_to(size);
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn extend_from_chars(&mut self, other: &[char]) {
for ch in other {
self.data.push(ColoredChar::builder().ch(*ch as u8).build());
}
}
pub fn extend_from_bytes(&mut self, other: &[u8]) {
for ch in other {
self.data.push(ColoredChar::builder().ch(*ch).build());
}
}
pub fn extend_from_slice(&mut self, other: &[ColoredChar]) {
self.data.extend_from_slice(other);
}
pub fn extend_from_other(&mut self, other: &ColoredString) {
self.data.extend_from_slice(&other.data);
}
pub fn extend_from_str(&mut self, other: &ColoredStr) {
self.data.extend_from_slice(other.get_all_chars());
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn truncate(&mut self, len: usize) {
self.data.truncate(len);
}
pub fn drain<R>(&mut self, range: R) -> ColoredStringIter
where
R: RangeBounds<usize>
{
ColoredString::new_from_inner(self.data.drain(range).as_slice()).into_iter()
}
pub fn append(&mut self, other: &mut ColoredString) {
self.data.append(&mut other.data);
}
pub fn reserve(&mut self, additional: usize) {
self.data.reserve(additional);
}
pub fn bytes_iter(&self) -> ColoredStringBytesIter {
ColoredStringBytesIter::new(self)
}
pub fn chars_iter(&self) -> ColoredStringCharIter {
ColoredStringCharIter::new(self)
}
pub fn get_all_chars(&self) -> &[ColoredChar] {
&self.data
}
pub fn to_colored_string(&self) -> ColoredString {
ColoredString::new_from_inner(&self.data)
}
pub fn to_string(&self) -> String {
self.data.iter().fold(String::with_capacity(self.len()), |mut output, ch| {
output.push(ch.get_char() as char);
output
})
}
pub fn replace_char(&mut self, prev: u8, with: u8) {
for ch in self.data.iter_mut() {
if ch.get_char() == prev {
ch.set_char(with);
}
}
}
pub fn replace_background_color(&mut self, prev: Color, with: Color) {
for ch in self.data.iter_mut() {
if ch.get_background_color() == prev {
ch.set_background_color(with);
}
}
}
pub fn replace_foreground_color(&mut self, prev: Color, with: Color) {
for ch in self.data.iter_mut() {
if ch.get_foreground_color() == prev {
ch.set_foreground_color(with);
}
}
}
pub fn replace_attribute(&mut self, prev: DisplayAttribute, with: DisplayAttribute) {
for ch in self.data.iter_mut() {
if ch.get_attribute() == prev {
ch.set_attribute(with);
}
}
}
pub fn set_all_foreground_colors(&mut self, color: Color) {
self.data.iter_mut().for_each(|ch| {
ch.set_foreground_color(color);
});
}
pub fn set_all_background_colors(&mut self, color: Color) {
self.data.iter_mut().for_each(|ch| {
ch.set_background_color(color);
});
}
pub fn set_all_attributes(&mut self, attribute: DisplayAttribute) {
self.data.iter_mut().for_each(|ch| {
ch.set_attribute(attribute);
});
}
pub fn as_str(&self) -> ColoredStr {
ColoredStr::new(&self.data)
}
}
impl Extend<u8> for ColoredString {
fn extend<T: IntoIterator<Item=u8>>(&mut self, iter: T) {
for ch in iter {
self.push(ColoredChar::builder().ch(ch).build());
}
}
}
impl Extend<ColoredChar> for ColoredString {
fn extend<T: IntoIterator<Item=ColoredChar>>(&mut self, iter: T) {
for ch in iter {
self.push(ch);
}
}
}
impl IntoIterator for ColoredString {
type Item = ColoredChar;
type IntoIter = ColoredStringIter;
fn into_iter(self) -> Self::IntoIter {
ColoredStringIter::new(self)
}
}
impl<'a> IntoIterator for &'a ColoredString {
type Item = &'a ColoredChar;
type IntoIter = ColoredStringIterRef<'a>;
fn into_iter(self) -> Self::IntoIter {
ColoredStringIterRef::new(self)
}
}
impl Into<String> for ColoredString {
fn into(self) -> String {
let mut output = String::with_capacity(self.len());
for ch in self.data {
output.push(ch.get_char() as char);
}
output
}
}
impl From<String> for ColoredString {
fn from(s: String) -> Self {
ColoredString::new_from_str(&s)
}
}
impl From<&str> for ColoredString {
fn from(s: &str) -> Self {
ColoredString::new_from_str(s)
}
}
impl PartialEq<String> for ColoredString {
fn eq(&self, other: &String) -> bool {
self.eq(other.as_str())
}
}
impl PartialEq<str> for ColoredString {
fn eq(&self, other: &str) -> bool {
for (i, ch) in other.chars().enumerate() {
if i >= self.len() {
return false;
}
if ch != self.get(i).unwrap().get_char() as char {
return false;
}
}
true
}
}
impl PartialEq for ColoredString {
fn eq(&self, other: &Self) -> bool {
self.data.eq(&other.data)
}
}
impl Eq for ColoredString {}
impl ops::Index<usize> for ColoredString {
type Output = ColoredChar;
fn index(&self, index: usize) -> &Self::Output {
match self.get(index) {
None => { panic!("Index {} out of range! (length: {})", index, self.len()); }
Some(ch) => {
&ch
}
}
}
}
impl ops::Index<RangeFrom<usize>> for ColoredString {
type Output = [ColoredChar];
fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl ops::Index<RangeTo<usize>> for ColoredString {
type Output = [ColoredChar];
fn index(&self, index: RangeTo<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl ops::Index<RangeFull> for ColoredString {
type Output = [ColoredChar];
fn index(&self, index: RangeFull) -> &Self::Output {
self.data.index(index)
}
}
impl ops::Index<RangeInclusive<usize>> for ColoredString {
type Output = [ColoredChar];
fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl ops::Index<RangeToInclusive<usize>> for ColoredString {
type Output = [ColoredChar];
fn index(&self, index: RangeToInclusive<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl ops::Index<Range<usize>> for ColoredString {
type Output = [ColoredChar];
fn index(&self, index: Range<usize>) -> &Self::Output {
self.data.index(index)
}
}
pub struct ColoredStr<'a> {
data: &'a [ColoredChar]
}
impl<'a> ColoredStr<'a> {
fn new(chars: &'a [ColoredChar]) -> Self {
Self {
data: chars,
}
}
pub fn get(&self, index: usize) -> Option<&ColoredChar> {
match self.data.get(index) {
None => { None }
Some(ch) => {
Some(ch)
}
}
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn bytes_iter(&self) -> ColoredStrBytesIter {
ColoredStrBytesIter::new(self)
}
pub fn chars_iter(&self) -> ColoredStrCharIter {
ColoredStrCharIter::new(self)
}
pub fn get_all_chars(&self) -> &[ColoredChar] {
&self.data
}
pub fn to_colored_string(&self) -> ColoredString {
ColoredString::new_from_inner(&self.data)
}
pub fn to_string(&self) -> String {
self.data.iter().fold(String::with_capacity(self.len()), |mut output, ch| {
output.push(ch.get_char() as char);
output
})
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a> From<&'a [ColoredChar]> for ColoredStr<'a> {
fn from(chars: &'a [ColoredChar]) -> Self {
ColoredStr::new(chars)
}
}
impl<'a> IntoIterator for &'a ColoredStr<'a> {
type Item = &'a ColoredChar;
type IntoIter = ColoredStrIter<'a>;
fn into_iter(self) -> Self::IntoIter {
ColoredStrIter::new(self)
}
}
impl<'a> Into<String> for ColoredStr<'a> {
fn into(self) -> String {
self.to_string()
}
}
impl<'a> PartialEq<String> for ColoredStr<'a> {
fn eq(&self, other: &String) -> bool {
self.eq(other.as_str())
}
}
impl<'a> PartialEq<str> for ColoredStr<'a> {
fn eq(&self, other: &str) -> bool {
for (i, ch) in other.chars().enumerate() {
if i >= self.len() {
return false;
}
if ch != self.get(i).unwrap().get_char() as char {
return false;
}
}
true
}
}
impl<'a> PartialEq for ColoredStr<'a> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<'a> PartialEq<ColoredString> for ColoredStr<'a> {
fn eq(&self, other: &ColoredString) -> bool {
self.data.eq(&other.data)
}
}
impl<'a> Eq for ColoredStr<'a> {}
impl<'a> ops::Index<usize> for ColoredStr<'a> {
type Output = ColoredChar;
fn index(&self, index: usize) -> &Self::Output {
match self.get(index) {
None => { panic!("Index {} out of range! (length: {})", index, self.len()); }
Some(ch) => {
&ch
}
}
}
}
impl<'a> ops::Index<RangeFrom<usize>> for ColoredStr<'a> {
type Output = [ColoredChar];
fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl<'a> ops::Index<RangeTo<usize>> for ColoredStr<'a> {
type Output = [ColoredChar];
fn index(&self, index: RangeTo<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl<'a> ops::Index<RangeFull> for ColoredStr<'a> {
type Output = [ColoredChar];
fn index(&self, index: RangeFull) -> &Self::Output {
self.data.index(index)
}
}
impl<'a> ops::Index<RangeInclusive<usize>> for ColoredStr<'a> {
type Output = [ColoredChar];
fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl<'a> ops::Index<RangeToInclusive<usize>> for ColoredStr<'a> {
type Output = [ColoredChar];
fn index(&self, index: RangeToInclusive<usize>) -> &Self::Output {
self.data.index(index)
}
}
impl<'a> ops::Index<Range<usize>> for ColoredStr<'a> {
type Output = [ColoredChar];
fn index(&self, index: Range<usize>) -> &Self::Output {
self.data.index(index)
}
}
pub struct ColoredStringIter {
colored_string: ColoredString,
}
impl ColoredStringIter {
fn new(colored_string: ColoredString) -> Self {
Self {
colored_string,
}
}
}
impl Iterator for ColoredStringIter {
type Item = ColoredChar;
fn next(&mut self) -> Option<Self::Item> {
if self.colored_string.is_empty() {
return None;
}
Some(self.colored_string.remove(0))
}
}
pub struct ColoredStringIterRef<'a> {
colored_string: &'a ColoredString,
index: usize,
}
impl<'a> ColoredStringIterRef<'a> {
fn new(colored_string: &'a ColoredString) -> Self {
Self {
colored_string,
index: 0,
}
}
}
impl<'a> Iterator for ColoredStringIterRef<'a> {
type Item = &'a ColoredChar;
fn next(&mut self) -> Option<Self::Item> {
let output = self.colored_string.get(self.index);
self.index += 1;
output
}
}
pub struct ColoredStringCharIter<'a> {
colored_string: ColoredStringIterRef<'a>,
}
impl<'a> ColoredStringCharIter<'a> {
fn new(colored_string: &'a ColoredString) -> Self {
Self {
colored_string: ColoredStringIterRef::new(colored_string)
}
}
}
impl<'a> Iterator for ColoredStringCharIter<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
match self.colored_string.next() {
None => { None }
Some(ch) => { Some(ch.get_char() as char) }
}
}
}
pub struct ColoredStringBytesIter<'a> {
colored_string: ColoredStringIterRef<'a>,
}
impl<'a> ColoredStringBytesIter<'a> {
fn new(colored_string: &'a ColoredString) -> Self {
Self {
colored_string: ColoredStringIterRef::new(colored_string)
}
}
}
impl<'a> Iterator for ColoredStringBytesIter<'a> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match self.colored_string.next() {
None => { None }
Some(ch) => { Some(ch.get_char()) }
}
}
}
pub struct ColoredStrIter<'a> {
colored_str: &'a ColoredStr<'a>,
index: usize,
}
impl<'a> ColoredStrIter<'a> {
fn new(colored_str: &'a ColoredStr) -> Self {
Self {
colored_str,
index: 0,
}
}
}
impl<'a> Iterator for ColoredStrIter<'a> {
type Item = &'a ColoredChar;
fn next(&mut self) -> Option<Self::Item> {
let output = self.colored_str.get(self.index);
if output.is_some() {
self.index += 1;
}
output
}
}
pub struct ColoredStrCharIter<'a> {
colored_str: ColoredStrIter<'a>,
}
impl<'a> ColoredStrCharIter<'a> {
fn new(colored_str: &'a ColoredStr) -> Self {
Self {
colored_str: ColoredStrIter::new(colored_str)
}
}
}
impl<'a> Iterator for ColoredStrCharIter<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
match self.colored_str.next() {
None => { None }
Some(ch) => { Some(ch.get_char() as char) }
}
}
}
pub struct ColoredStrBytesIter<'a> {
colored_str: ColoredStrIter<'a>,
}
impl<'a> ColoredStrBytesIter<'a> {
fn new(colored_string: &'a ColoredStr) -> Self {
Self {
colored_str: ColoredStrIter::new(colored_string)
}
}
}
impl<'a> Iterator for ColoredStrBytesIter<'a> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match self.colored_str.next() {
None => { None }
Some(ch) => { Some(ch.get_char()) }
}
}
}