styled_str/types/
spans.rs1use core::num::NonZeroUsize;
4
5use anstyle::Style;
6use compile_fmt::compile_panic;
7
8#[derive(Debug, Clone, Copy, PartialEq)]
10pub(crate) struct StyledSpan {
11 pub(crate) style: Style,
13 pub(crate) start: usize,
15 pub(crate) len: NonZeroUsize,
17}
18
19impl StyledSpan {
20 pub(crate) const DUMMY: Self = Self {
21 style: Style::new(),
22 start: 0,
23 len: NonZeroUsize::new(1).unwrap(),
24 };
25
26 pub(crate) const fn end(&self) -> usize {
27 self.start + self.len.get()
28 }
29
30 pub(crate) const fn extend_len(&mut self, add: usize) {
31 self.len = self.len.checked_add(add).expect("length overflow");
32 }
33
34 pub(crate) const fn shrink_len(&mut self, sub: usize) {
35 let new_len = self.len.get().checked_sub(sub).expect("length underflow");
36 self.len = NonZeroUsize::new(new_len).expect("length underflow");
37 }
38
39 pub(crate) fn can_contain(&self, needle: &Self) -> bool {
40 self.style == needle.style && self.len >= needle.len
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq)]
46pub struct SpanStr<'a> {
47 pub text: &'a str,
49 pub style: Style,
51}
52
53impl<'a> SpanStr<'a> {
54 pub const fn new(text: &'a str, style: Style) -> Self {
60 let text_bytes = text.as_bytes();
61 let mut pos = 0;
62 while pos < text_bytes.len() {
63 if text_bytes[pos] == 0x1b {
64 compile_panic!(
65 "text contains \\x1b escape, first at position ",
66 pos => compile_fmt::fmt::<usize>()
67 );
68 }
69 pos += 1;
70 }
71 Self { text, style }
72 }
73
74 pub const fn plain(text: &'a str) -> Self {
80 Self::new(text, Style::new())
81 }
82}