use core::num::NonZeroUsize;
use anstyle::Style;
use compile_fmt::compile_panic;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct StyledSpan {
pub(crate) style: Style,
pub(crate) start: usize,
pub(crate) len: NonZeroUsize,
}
impl StyledSpan {
pub(crate) const DUMMY: Self = Self {
style: Style::new(),
start: 0,
len: NonZeroUsize::new(1).unwrap(),
};
pub(crate) const fn end(&self) -> usize {
self.start + self.len.get()
}
pub(crate) const fn extend_len(&mut self, add: usize) {
self.len = self.len.checked_add(add).expect("length overflow");
}
pub(crate) const fn shrink_len(&mut self, sub: usize) {
let new_len = self.len.get().checked_sub(sub).expect("length underflow");
self.len = NonZeroUsize::new(new_len).expect("length underflow");
}
pub(crate) fn can_contain(&self, needle: &Self) -> bool {
self.style == needle.style && self.len >= needle.len
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SpanStr<'a> {
pub text: &'a str,
pub style: Style,
}
impl<'a> SpanStr<'a> {
pub const fn new(text: &'a str, style: Style) -> Self {
let text_bytes = text.as_bytes();
let mut pos = 0;
while pos < text_bytes.len() {
if text_bytes[pos] == 0x1b {
compile_panic!(
"text contains \\x1b escape, first at position ",
pos => compile_fmt::fmt::<usize>()
);
}
pos += 1;
}
Self { text, style }
}
pub const fn plain(text: &'a str) -> Self {
Self::new(text, Style::new())
}
}