1use crate::_private::NonExhaustive;
7use crate::{TableContext, TableData};
8use ratatui::buffer::Buffer;
9use ratatui::layout::Rect;
10use ratatui::style::{Style, Styled};
11use ratatui::text::Text;
12use ratatui::widgets::Widget;
13
14#[derive(Debug, Default, Clone)]
16pub(crate) struct TextTableData<'a> {
17 pub(crate) rows: Vec<Row<'a>>,
18}
19
20#[derive(Debug, Clone)]
22pub struct Row<'a> {
23 pub cells: Vec<Cell<'a>>,
24 pub top_margin: u16,
25 pub height: u16,
26 pub bottom_margin: u16,
27 pub style: Option<Style>,
28
29 pub non_exhaustive: NonExhaustive,
30}
31
32#[derive(Debug, Clone)]
34pub struct Cell<'a> {
35 pub content: Text<'a>,
36 pub style: Option<Style>,
37
38 pub non_exhaustive: NonExhaustive,
39}
40
41impl<'a> TableData<'a> for TextTableData<'a> {
42 fn rows(&self) -> usize {
43 self.rows.len()
44 }
45
46 fn row_height(&self, r: usize) -> u16 {
47 if let Some(row) = self.rows.get(r) {
48 row.top_margin + row.height + row.bottom_margin
49 } else {
50 0
51 }
52 }
53
54 fn row_style(&self, r: usize) -> Option<Style> {
55 if let Some(row) = self.rows.get(r) {
56 row.style
57 } else {
58 None
59 }
60 }
61
62 #[allow(clippy::collapsible_if)]
63 fn render_cell(&self, _ctx: &TableContext, c: usize, r: usize, area: Rect, buf: &mut Buffer) {
64 if let Some(row) = self.rows.get(r) {
65 if let Some(cell) = row.cell(c) {
66 if let Some(style) = cell.style {
67 buf.set_style(area, style);
68 }
69 cell.content.clone().render(area, buf);
70 }
71 }
72 }
73}
74
75impl Default for Row<'_> {
76 fn default() -> Self {
77 Self {
78 cells: Default::default(),
79 top_margin: 0,
80 height: 0,
81 bottom_margin: 0,
82 style: Default::default(),
83 non_exhaustive: NonExhaustive,
84 }
85 }
86}
87
88impl Styled for Row<'_> {
89 type Item = Self;
90
91 fn style(&self) -> Style {
92 self.style.unwrap_or_default()
93 }
94
95 fn set_style<S: Into<Style>>(mut self, style: S) -> Self::Item {
96 self.style = Some(style.into());
97 self
98 }
99}
100
101impl<'a, Item> FromIterator<Item> for Row<'a>
102where
103 Item: Into<Cell<'a>>,
104{
105 fn from_iter<T: IntoIterator<Item = Item>>(cells: T) -> Self {
106 Self::new(cells)
107 }
108}
109
110impl<'a> Row<'a> {
111 pub fn new<T>(cells: T) -> Self
113 where
114 T: IntoIterator,
115 T::Item: Into<Cell<'a>>,
116 {
117 let mut s = Self {
118 cells: cells.into_iter().map(|v| v.into()).collect(),
119 height: 1,
120 ..Default::default()
121 };
122 if let Some(height) = s.cells.iter().map(|v| v.content.height()).max() {
124 s.height = height as u16;
125 }
126 s
127 }
128
129 pub fn cells<T>(mut self, cells: T) -> Self
131 where
132 T: IntoIterator,
133 T::Item: Into<Cell<'a>>,
134 {
135 self.cells = cells.into_iter().map(Into::into).collect();
136 self
137 }
138
139 #[inline]
141 pub fn height(mut self, height: u16) -> Self {
142 self.height = height;
143 self
144 }
145
146 pub fn top_margin(mut self, margin: u16) -> Self {
148 self.top_margin = margin;
149 self
150 }
151
152 pub fn bottom_margin(mut self, margin: u16) -> Self {
154 self.bottom_margin = margin;
155 self
156 }
157
158 pub fn style(mut self, style: Option<Style>) -> Self {
160 self.style = style;
161 self
162 }
163
164 pub fn cell<'b: 'a>(&'b self, c: usize) -> Option<&'a Cell<'a>> {
166 if let Some(t) = self.cells.get(c) {
167 Some(t)
168 } else {
169 None
170 }
171 }
172}
173
174impl Default for Cell<'_> {
175 fn default() -> Self {
176 Self {
177 content: Default::default(),
178 style: Default::default(),
179 non_exhaustive: NonExhaustive,
180 }
181 }
182}
183
184impl<'a, T> From<T> for Cell<'a>
185where
186 T: Into<Text<'a>>,
187{
188 fn from(value: T) -> Self {
189 Self {
190 content: value.into(),
191 style: Default::default(),
192 non_exhaustive: NonExhaustive,
193 }
194 }
195}
196
197impl Styled for Cell<'_> {
198 type Item = Self;
199
200 fn style(&self) -> Style {
201 self.style.unwrap_or_default()
202 }
203
204 fn set_style<S: Into<Style>>(mut self, style: S) -> Self {
205 self.style = Some(style.into());
206 self
207 }
208}
209
210impl<'a> Cell<'a> {
211 pub fn new<T>(content: T) -> Self
213 where
214 T: Into<Text<'a>>,
215 {
216 Self {
217 content: content.into(),
218 style: Default::default(),
219 non_exhaustive: NonExhaustive,
220 }
221 }
222
223 pub fn content<T>(mut self, content: T) -> Self
225 where
226 T: Into<Text<'a>>,
227 {
228 self.content = content.into();
229 self
230 }
231
232 pub fn style(mut self, style: Option<Style>) -> Self {
234 self.style = style;
235 self
236 }
237}