lexa_logger/layout/
cell.rs1use std::borrow::Cow;
12use std::cmp;
13use std::collections::HashSet;
14
15use unicode_width::UnicodeWidthStr;
16
17use super::style::Alignment;
18
19#[derive(Debug)]
24#[derive(Clone)]
25pub struct Cell<'d>
26{
27 pub(crate) alignment: Alignment,
28 pub(crate) colspan: usize,
29 data: Cow<'d, str>,
30 padding: bool,
31}
32
33impl<'d> Cell<'d>
38{
39 pub fn new(data: impl ToString) -> Self
40 {
41 Self {
42 alignment: Alignment::Left,
43 colspan: 1,
44 data: Cow::from(data.to_string()),
45 padding: true,
46 }
47 }
48
49 pub fn with_alignment(mut self, alignment: Alignment) -> Self
50 {
51 self.alignment = alignment;
52 self
53 }
54}
55
56impl<'d> Cell<'d>
57{
58 pub(crate) fn width(&self) -> usize
59 {
60 self.wrapped_content(std::usize::MAX)
61 .iter()
62 .fold(0, |max, text| cmp::max(max, str_len(text)))
63 }
64
65 pub(crate) fn split_width(&self) -> f32
66 {
67 self.width() as f32 / self.colspan as f32
68 }
69
70 pub(crate) fn min_width(&self) -> usize
71 {
72 let max_char_width = self.data.chars().fold(0, |max, ch| cmp::max(max, ch.len_utf8()));
73
74 if self.padding {
75 max_char_width + ' '.len_utf8() * 2
76 } else {
77 max_char_width
78 }
79 }
80
81 pub(crate) fn wrapped_content(&self, width: usize) -> Vec<String>
82 {
83 let pad_char = if self.padding { ' ' } else { '\0' };
84
85 let hidden: HashSet<usize> = STRIP_ANSI_RE
86 .find_iter(&self.data)
87 .flat_map(|m| m.start()..m.end())
88 .collect();
89
90 let mut output = Vec::default();
91
92 let mut temporary_buffer = String::default();
93
94 temporary_buffer.push(pad_char);
95
96 let mut byte_index = 0;
97
98 for ch in self.data.chars() {
99 if !hidden.contains(&byte_index)
100 && (str_len(&temporary_buffer) >= width - pad_char.len_utf8() || ch == '\n')
101 {
102 temporary_buffer.push(pad_char);
103 output.push(temporary_buffer);
104 temporary_buffer = String::new();
105 temporary_buffer.push(pad_char);
106
107 if ch == '\n' {
108 byte_index += 1;
109 continue;
110 }
111 }
112
113 byte_index += ch.len_utf8();
114
115 temporary_buffer.push(ch);
116 }
117
118 temporary_buffer.push(pad_char);
119
120 output.push(temporary_buffer);
121
122 output
123 }
124}
125
126pub(crate) fn str_len(string: &str) -> usize
127{
128 let stripped = STRIP_ANSI_RE.replace_all(string, "");
129 stripped.width()
130}
131
132lazy_static::lazy_static! {
133 static ref STRIP_ANSI_RE: regex::Regex = regex::Regex::new(
136 r"[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]"
137 )
138 .unwrap();
139}