use std::{borrow::Cow, cell::Cell, fmt::Display};
mod term_text_metadata;
mod term_text_span;
mod term_text_spans;
pub use self::{term_text_metadata::*, term_text_span::*, term_text_spans::*};
#[derive(Debug, Clone)]
pub struct TermText<'a> {
text: Cow<'a, str>,
metadata: Cell<Option<TermTextMetadata>>,
}
impl<'a> TermText<'a> {
pub fn new(text: impl Into<Cow<'a, str>>) -> Self {
Self {
text: text.into(),
metadata: Cell::default(),
}
}
pub unsafe fn from_metadata(
text: impl Into<Cow<'a, str>>,
metadata: TermTextMetadata,
) -> Self {
Self {
text: text.into(),
metadata: Cell::new(Some(metadata)),
}
}
pub fn chached(text: impl Into<Cow<'a, str>>) -> Self {
let res = Self::new(text);
res.get_metadata();
res
}
pub fn reference(&self) -> TermText<'_> {
TermText {
text: self.as_str().into(),
metadata: Cell::new(self.metadata.get()),
}
}
pub fn as_str(&self) -> &str {
&self.text
}
pub fn try_get_metadata(&self) -> Option<TermTextMetadata> {
self.metadata.get()
}
#[inline]
pub fn get_metadata(&self) -> TermTextMetadata {
if let Some(md) = self.metadata.get() {
md
} else {
let md = TermTextMetadata::from_text(&self.text);
self.metadata.set(Some(md));
md
}
}
pub fn byte_cnt(&self) -> usize {
self.text.len()
}
pub fn char_cnt(&self) -> usize {
self.get_metadata().chars
}
pub fn display_char_cnt(&self) -> usize {
self.get_metadata().display_chars()
}
pub fn display_bytes_cnt(&self) -> usize {
let meta = self.get_metadata();
self.byte_cnt() - meta.control_bytes
}
pub fn control_char_cnt(&self) -> usize {
self.get_metadata().control_chars
}
pub fn control_bytes_cnt(&self) -> usize {
self.get_metadata().control_bytes
}
pub fn columns(&self) -> usize {
self.get_metadata().columns
}
pub fn spans(&self) -> TermTextSpans<'_> {
TermTextSpans::new(&self.text)
}
#[inline]
pub fn strip_control(&self) -> String {
let mut res = String::new();
let meta = if let Some(meta) = self.metadata.get() {
meta
} else {
let mut meta = TermTextMetadata::default();
for span in self.spans().filter(|s| !s.is_control()) {
res.push_str(span.text());
meta.chars += span.chars();
if span.is_control() {
meta.control_chars += span.chars();
meta.control_bytes += span.text().len();
}
}
self.metadata.set(Some(meta));
meta
};
res.reserve_exact(self.text.len() - meta.control_bytes);
for span in self.spans().filter(|s| !s.is_control()) {
res.push_str(span.text());
}
res
}
pub fn to_string_cache(&self) -> String {
if self.metadata.get().is_some() {
self.to_string()
} else {
let mut res = String::with_capacity(self.text.len());
let mut meta = TermTextMetadata::default();
for span in self.spans() {
meta.chars += span.chars();
res.push_str(span.text());
if span.is_control() {
meta.control_bytes += span.text().len();
meta.control_chars += span.chars();
}
}
self.metadata.set(Some(meta));
res
}
}
pub fn as_cow(&self) -> &Cow<'a, str> {
&self.text
}
pub fn to_owned(self) -> TermText<'static> {
match self.text {
Cow::Owned(s) => TermText {
text: Cow::Owned(s),
metadata: self.metadata,
},
Cow::Borrowed(_) => {
let text = self.to_string();
TermText {
text: Cow::Owned(text),
metadata: self.metadata,
}
}
}
}
}
impl AsRef<str> for TermText<'_> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Display for TermText<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl From<String> for TermText<'_> {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl<'a> From<&'a str> for TermText<'a> {
fn from(value: &'a str) -> Self {
Self::new(value)
}
}
impl<'a> From<TermText<'a>> for String {
fn from(value: TermText<'a>) -> Self {
value.text.into()
}
}
impl<'a> From<TermText<'a>> for Cow<'a, str> {
fn from(value: TermText<'a>) -> Self {
value.text
}
}
impl Default for TermText<'_> {
fn default() -> Self {
Self {
text: Default::default(),
metadata: Cell::new(Some(Default::default())),
}
}
}