1use self::HorizontalAlignment::*;
2use crate::cell::*;
3use crate::Cells;
4use crate::CellsFormatter;
5use crate::CellsSchema;
6use crate::CellsWrite;
7use crate::DefaultCellsSchema;
8use derive_ex::derive_ex;
9use std::borrow::Borrow;
10use std::cmp::*;
11use std::collections::HashMap;
12use std::fmt::*;
13use std::ops::Deref;
14use unicode_width::UnicodeWidthStr;
15
16struct GridLayout {
17 depth: usize,
18 depth_max: usize,
19 styles: Vec<ColumnStyle>,
20}
21impl GridLayout {
22 pub fn from_schema<T: ?Sized>(schema: &dyn CellsSchema<Source = T>) -> Self {
23 let mut this = GridLayout::new();
24 schema.fmt(&mut CellsFormatter::new(&mut this, None));
25 this.styles.pop();
26 this
27 }
28 fn new() -> Self {
29 Self {
30 depth: 0,
31 depth_max: 0,
32 styles: Vec::new(),
33 }
34 }
35 fn set_column_end_style(&mut self) {
36 if let Some(last) = self.styles.last_mut() {
37 last.column_end = true;
38 }
39 }
40}
41impl CellsWrite for GridLayout {
42 fn content(&mut self, _cell: Option<&dyn RawCell>, stretch: bool) {
43 self.styles.push(ColumnStyle {
44 column_end: false,
45 stretch,
46 });
47 }
48 fn content_start(&mut self, _cell: &dyn RawCell) {}
49 fn content_end(&mut self, _cell: &dyn RawCell) {}
50 fn column_start(&mut self, _header: &dyn RawCell) {
51 self.set_column_end_style();
52 self.depth += 1;
53 self.depth_max = max(self.depth_max, self.depth);
54 }
55
56 fn column_end(&mut self, _header: &dyn RawCell) {
57 self.depth -= 1;
58 self.set_column_end_style()
59 }
60}
61
62struct HeaderWriter<'a, 'b> {
63 b: &'a mut RowBuilder<'b>,
64 depth: usize,
65 target: usize,
66 column: usize,
67 column_last: usize,
68}
69impl<'a, 'b> HeaderWriter<'a, 'b> {
70 fn new(b: &'a mut RowBuilder<'b>, target: usize) -> Self {
71 Self {
72 b,
73 depth: 0,
74 target,
75 column: 0,
76 column_last: 0,
77 }
78 }
79
80 fn push_cell(&mut self, cell: impl RawCell) {
81 let colspan = self.column - self.column_last;
82 self.b.push_with_colspan(cell, colspan);
83 self.column_last = self.column;
84 }
85}
86impl CellsWrite for HeaderWriter<'_, '_> {
87 fn content(&mut self, _cell: Option<&dyn RawCell>, _stretch: bool) {
88 self.column += 1;
89 }
90 fn content_start(&mut self, _cell: &dyn RawCell) {}
91 fn content_end(&mut self, _cell: &dyn RawCell) {}
92 fn column_start(&mut self, _header: &dyn RawCell) {
93 if self.depth <= self.target {
94 self.push_cell(Cell::empty());
95 }
96 self.depth += 1;
97 }
98 fn column_end(&mut self, header: &dyn RawCell) {
99 self.depth -= 1;
100 if self.depth == self.target {
101 let style = CellStyle {
102 align_h: Some(HorizontalAlignment::Center),
103 };
104 let header = Cell::new(header).with_base_style(style);
105 self.push_cell(header);
106 }
107 }
108}
109impl Drop for HeaderWriter<'_, '_> {
110 fn drop(&mut self) {
111 self.push_cell("");
112 }
113}
114struct BodyWriter<'a, 'b> {
115 b: &'a mut RowBuilder<'b>,
116 colspan: Option<usize>,
117}
118
119impl<'a, 'b> BodyWriter<'a, 'b> {
120 fn new(b: &'a mut RowBuilder<'b>) -> Self {
121 Self { b, colspan: None }
122 }
123}
124
125impl CellsWrite for BodyWriter<'_, '_> {
126 fn content(&mut self, cell: Option<&dyn RawCell>, _stretch: bool) {
127 if let Some(colspan) = &mut self.colspan {
128 *colspan += 1;
129 } else {
130 self.b.push(cell);
131 }
132 }
133 fn content_start(&mut self, _cell: &dyn RawCell) {
134 assert!(self.colspan.is_none());
135 self.colspan = Some(0);
136 }
137 fn content_end(&mut self, cell: &dyn RawCell) {
138 let colspan = self.colspan.take().unwrap();
139 self.b.push_with_colspan(cell, colspan);
140 }
141
142 fn column_start(&mut self, _header: &dyn RawCell) {}
143 fn column_end(&mut self, _header: &dyn RawCell) {}
144}
145
146#[derive_ex(Default)]
175#[default(Self::new())]
176pub struct GridBuilder {
177 s: String,
178 cells: Vec<CellEntry>,
179 rows: Vec<RowEntry>,
180 columns: usize,
181 pub column_styles: Vec<ColumnStyle>,
182}
183
184struct CellEntry {
185 s_idx: usize,
186 width: usize,
187 colspan: usize,
188 style: CellStyle,
189}
190struct RowEntry {
191 cells_idx: usize,
192 has_separator: bool,
193}
194
195impl GridBuilder {
196 pub fn new() -> Self {
198 GridBuilder {
199 s: String::new(),
200 cells: Vec::new(),
201 rows: Vec::new(),
202 columns: 0,
203 column_styles: Vec::new(),
204 }
205 }
206
207 pub fn from_iter_with_schema<T>(
208 source: impl IntoIterator<Item = impl Borrow<T>>,
209 schema: impl CellsSchema<Source = T>,
210 ) -> Self {
211 let mut this = Self::new();
212 this.extend_header_with_schema(&schema);
213 this.extend_body_with_schema(source, &schema);
214 this
215 }
216
217 pub fn push(&mut self, f: impl FnOnce(&mut RowBuilder)) {
219 let cells_idx = self.cells.len();
220 f(&mut RowBuilder {
221 grid: self,
222 cells_idx,
223 })
224 }
225
226 pub fn push_separator(&mut self) {
228 if let Some(row) = self.rows.last_mut() {
229 row.has_separator = true;
230 }
231 }
232
233 pub fn extend_header<T: ?Sized + Cells>(&mut self) {
234 self.extend_header_with_schema::<T>(&DefaultCellsSchema::default());
235 }
236
237 pub fn extend_header_with_schema<T: ?Sized>(&mut self, schema: impl CellsSchema<Source = T>) {
238 let layout = GridLayout::from_schema(&schema);
239 self.column_styles = layout.styles;
240 for target in 0..layout.depth_max {
241 self.push(|b| {
242 schema.fmt(&mut CellsFormatter::new(
243 &mut HeaderWriter::new(b, target),
244 None,
245 ))
246 });
247 self.push_separator();
248 }
249 }
250
251 pub fn push_body(&mut self, source: &impl Cells) {
252 self.push_body_with_schema(source, &DefaultCellsSchema::default());
253 }
254 pub fn push_body_with_schema<T: ?Sized>(
255 &mut self,
256 source: &T,
257 schema: impl CellsSchema<Source = T>,
258 ) {
259 self.push(|b| {
260 b.extend_with_schema(source, &schema);
261 });
262 }
263 pub fn extend_body(&mut self, source: impl IntoIterator<Item = impl Cells>) {
264 self.extend_body_with_schema(source, &DefaultCellsSchema::default());
265 }
266 pub fn extend_body_with_schema<T>(
267 &mut self,
268 source: impl IntoIterator<Item = impl Borrow<T>>,
269 schema: impl CellsSchema<Source = T>,
270 ) {
271 for source in source {
272 self.push(|b| {
273 b.extend_with_schema(source.borrow(), &schema);
274 });
275 }
276 }
277
278 fn push_cell<S: RawCell>(&mut self, cell: S, colspan: usize) {
279 let s_idx = self.s.len();
280 cell.fmt(&mut self.s);
281 self.cells.push(CellEntry {
282 s_idx,
283 width: self.s[s_idx..].width(),
284 colspan,
285 style: cell.style().or(cell.style_for_body()),
286 });
287 }
288 fn get_width(&self, widths: &[usize], column: usize, colspan: usize) -> usize {
289 assert!(colspan >= 1);
290 let mut result = widths[column];
291 for i in 1..colspan {
292 if self.has_border(column + i) {
293 result += 3;
294 }
295 result += widths[column + i];
296 }
297 result
298 }
299 fn has_border(&self, n: usize) -> bool {
300 if n == 0 {
301 false
302 } else if n >= self.columns {
303 true
304 } else {
305 self.column_style(n - 1).column_end
306 }
307 }
308 fn has_left_padding(&self, n: usize) -> bool {
309 if n == 0 {
310 true
311 } else {
312 self.has_border(n)
313 }
314 }
315 fn has_right_padding(&self, n: usize) -> bool {
316 if n == self.columns {
317 true
318 } else {
319 self.has_border(n + 1)
320 }
321 }
322
323 fn column_style(&self, column: usize) -> &ColumnStyle {
324 self.column_styles
325 .get(column)
326 .unwrap_or(&ColumnStyle::DEFAULT)
327 }
328 fn stretch_count(&self, column: usize, colspan: usize) -> usize {
329 let mut count = 0;
330 for i in 0..colspan {
331 if self.column_style(column + i).stretch {
332 count += 1;
333 }
334 }
335 count
336 }
337
338 fn get_widths(&self) -> Vec<usize> {
339 #[derive(PartialEq, Eq, Hash)]
340 struct ColRange {
341 colspan: usize,
342 column: usize,
343 }
344
345 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
346 struct Block {
347 stretch: usize,
348 colspan: usize,
349 column: usize,
350 width: usize,
351 }
352
353 let mut widths = vec![0; self.columns];
354 let mut blocks = HashMap::new();
355 for row in self.rows() {
356 for c in row {
357 let e = if c.colspan == 1 {
358 &mut widths[c.column]
359 } else {
360 let key = ColRange {
361 colspan: c.colspan,
362 column: c.column,
363 };
364 blocks.entry(key).or_insert(0)
365 };
366 *e = max(*e, c.width);
367 }
368 }
369 let mut blocks: Vec<_> = blocks
370 .into_iter()
371 .map(|c| Block {
372 stretch: self.stretch_count(c.0.column, c.0.colspan),
373 colspan: c.0.colspan,
374 column: c.0.column,
375 width: c.1,
376 })
377 .collect();
378 blocks.sort();
379
380 let mut expand_cols = Vec::new();
381 for b in blocks {
382 let mut width_sum = self.get_width(&widths, b.column, b.colspan);
383 let start = if b.stretch == 0 {
384 b.column
385 } else {
386 (b.column..b.column + b.colspan)
387 .find(|&column| self.column_style(column).stretch)
388 .unwrap()
389 };
390
391 while width_sum < b.width {
392 expand_cols.clear();
393 expand_cols.push(start);
394 let mut min_width = widths[start];
395 let mut next_width = usize::max_value();
396 #[allow(clippy::needless_range_loop)]
397 for column in start + 1..b.column + b.colspan {
398 if b.stretch == 0 || self.column_style(column).stretch {
399 let width = widths[column];
400 if width < min_width {
401 expand_cols.clear();
402 next_width = min_width;
403 min_width = width;
404 }
405 if width == min_width {
406 expand_cols.push(column);
407 }
408 }
409 }
410 for i in 0..expand_cols.len() {
411 let count = expand_cols.len() - i;
412 let expand_width_all = b.width - width_sum;
413 let expand_width = (expand_width_all + count - 1) / count;
414 let expand_width = min(expand_width, next_width - min_width);
415 width_sum += expand_width;
416 widths[expand_cols[i]] += expand_width;
417 }
418 }
419 }
420 widths
421 }
422 fn row(&self, row: usize) -> Option<Cursor> {
423 if row < self.rows.len() {
424 Some(Cursor {
425 grid: self,
426 column: 0,
427 idx: self.cells_idx(row),
428 end: self.cells_idx(row + 1),
429 })
430 } else {
431 None
432 }
433 }
434 fn rows(&self) -> impl Iterator<Item = Cursor> {
435 (0..self.rows.len()).map(|row| self.row(row).unwrap())
436 }
437
438 fn cells_idx(&self, row: usize) -> usize {
439 if let Some(row) = self.rows.get(row) {
440 row.cells_idx
441 } else {
442 self.cells.len()
443 }
444 }
445 fn s_idx(&self, cells_idx: usize) -> usize {
446 if let Some(cell) = self.cells.get(cells_idx) {
447 cell.s_idx
448 } else {
449 self.s.len()
450 }
451 }
452}
453
454impl Display for GridBuilder {
455 fn fmt(&self, f: &mut Formatter) -> Result {
456 let widths = self.get_widths();
457 for row in 0..self.rows.len() {
458 if self.has_border(0) {
459 write!(f, "|")?;
460 }
461 for c in self.row(row).unwrap() {
462 let width = self.get_width(&widths, c.column, c.colspan);
463 if self.has_left_padding(c.column) {
464 write!(f, " ")?;
465 }
466 let p = width - c.width;
467 match c.style.align_h.unwrap_or(Left) {
468 Left => write!(f, "{0}{1:<p$}", c.s, "", p = p),
469 Right => write!(f, "{1:<p$}{0}", c.s, "", p = p),
470 Center => {
471 let lp = p / 2;
472 let rp = p - lp;
473 write!(f, "{1:<lp$}{0}{1:<rp$}", c.s, "", lp = lp, rp = rp)
474 }
475 }?;
476 if self.has_right_padding(c.column + c.colspan - 1) {
477 write!(f, " ")?;
478 }
479 if self.has_border(c.column + c.colspan) {
480 write!(f, "|")?;
481 }
482 }
483 writeln!(f)?;
484 if self.rows[row].has_separator {
485 let mut cs = [self.row(row), self.row(row + 1)];
486 for (column, _) in widths.iter().enumerate() {
487 if self.has_left_padding(column) {
488 write!(f, "-")?;
489 }
490 write!(f, "{:-<f$}", "", f = widths[column])?;
491 if self.has_right_padding(column) {
492 write!(f, "-")?;
493 }
494 for c in cs.iter_mut().flatten() {
495 while c.column <= column && c.next().is_some() {}
496 }
497 if self.has_border(column + 1) {
498 if cs.iter().flatten().all(|x| x.column == column + 1) {
499 write!(f, "|")?;
500 } else {
501 write!(f, "-")?;
502 }
503 }
504 }
505 writeln!(f)?;
506 }
507 }
508 Ok(())
509 }
510}
511impl Debug for GridBuilder {
512 fn fmt(&self, f: &mut Formatter) -> Result {
513 Display::fmt(self, f)
514 }
515}
516impl<T: Cells> FromIterator<T> for GridBuilder {
517 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
518 let mut b = Self::new();
519 b.extend_header::<T>();
520 b.extend_body(iter);
521 b
522 }
523}
524
525pub struct RowBuilder<'a> {
529 grid: &'a mut GridBuilder,
530 cells_idx: usize,
531}
532
533impl RowBuilder<'_> {
534 pub fn push(&mut self, cell: impl RawCell) {
536 self.grid.push_cell(cell, 1);
537 }
538
539 pub fn push_with_colspan(&mut self, cell: impl RawCell, colspan: usize) {
546 if colspan != 0 {
547 self.grid.push_cell(cell, colspan);
548 }
549 }
550
551 pub fn extend<T: ?Sized + Cells>(&mut self, source: &T) {
552 T::fmt(&mut CellsFormatter::new(
553 &mut BodyWriter::new(self),
554 Some(source),
555 ))
556 }
557 pub fn extend_with_schema<T: ?Sized>(
558 &mut self,
559 source: &T,
560 schema: impl CellsSchema<Source = T>,
561 ) {
562 schema.fmt(&mut CellsFormatter::new(
563 &mut BodyWriter::new(self),
564 Some(source),
565 ))
566 }
567}
568impl Drop for RowBuilder<'_> {
569 fn drop(&mut self) {
570 let mut columns = 0;
571 for cell in &self.grid.cells[self.cells_idx..] {
572 columns += cell.colspan;
573 }
574 self.grid.columns = max(self.grid.columns, columns);
575 self.grid.rows.push(RowEntry {
576 cells_idx: self.cells_idx,
577 has_separator: false,
578 });
579 }
580}
581
582struct Cursor<'a> {
583 grid: &'a GridBuilder,
584 column: usize,
585 idx: usize,
586 end: usize,
587}
588impl<'a> Iterator for Cursor<'a> {
589 type Item = CellRef<'a>;
590
591 fn next(&mut self) -> Option<Self::Item> {
592 if self.idx == self.end {
593 None
594 } else {
595 let g = self.grid;
596 let r = CellRef {
597 cell: &g.cells[self.idx],
598 s: &g.s[g.s_idx(self.idx)..g.s_idx(self.idx + 1)],
599 column: self.column,
600 };
601 self.column += r.colspan;
602 self.idx += 1;
603 Some(r)
604 }
605 }
606}
607
608struct CellRef<'a> {
609 cell: &'a CellEntry,
610 s: &'a str,
611 column: usize,
612}
613impl<'a> Deref for CellRef<'a> {
614 type Target = &'a CellEntry;
615 fn deref(&self) -> &Self::Target {
616 &self.cell
617 }
618}
619
620#[derive(Debug, Clone, Eq, PartialEq)]
622#[derive_ex(Default)]
623#[default(Self::DEFAULT)]
624pub struct ColumnStyle {
625 pub column_end: bool,
655
656 pub stretch: bool,
692}
693impl ColumnStyle {
694 const DEFAULT: Self = Self {
695 column_end: true,
696 stretch: false,
697 };
698}