Skip to main content

ctl_core/
table.rs

1//! Compatibility helpers over the semantic document renderer.
2//!
3//! New consumers should compose [`Fields`](crate::Fields) and
4//! [`Table`](crate::Table) inside a [`Document`](crate::Document).
5
6use crate::color::ColorMode;
7use crate::document::{Document, Fields, Table};
8use crate::render::RenderOptions;
9
10/// Two-column token/value fields. Tokens are styled unless `color` is never.
11#[must_use]
12pub fn kv(
13    color: ColorMode,
14    rows: impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
15) -> String {
16    let fields = rows
17        .into_iter()
18        .fold(Fields::new(), |fields, (token, value)| {
19            fields.row(token.as_ref(), value.as_ref())
20        });
21    Document::new()
22        .fields(fields)
23        .render(RenderOptions::new(color))
24        .trim_end()
25        .to_owned()
26}
27
28/// Headered grid. Headers and the first column carry semantic styles.
29#[must_use]
30pub fn grid(
31    color: ColorMode,
32    headers: &[&str],
33    rows: impl IntoIterator<Item = Vec<String>>,
34) -> String {
35    let table = rows.into_iter().fold(
36        Table::new(headers.iter().copied()).token_column(0),
37        Table::row,
38    );
39    Document::new()
40        .table(table)
41        .render(RenderOptions::new(color))
42        .trim_end()
43        .to_owned()
44}
45
46#[cfg(test)]
47mod tests {
48    use super::{grid, kv};
49    use crate::color::ColorMode;
50
51    #[test]
52    fn kv_aligns_values_in_one_column() {
53        let out = kv(
54            ColorMode::Never,
55            [("crate", "demo@0.0.1"), ("package", "@org/pkg@0.0.1")],
56        );
57        assert!(out.contains("crate"), "{out}");
58        assert!(out.contains("demo@0.0.1"), "{out}");
59        assert!(out.contains("package"), "{out}");
60        let starts: Vec<_> = out
61            .lines()
62            .map(|line| line.find("demo").or_else(|| line.find("@org")))
63            .collect();
64        assert_eq!(starts, [Some(9), Some(9)], "{out}");
65        assert!(!out.contains('\u{1b}'), "{out:?}");
66    }
67
68    #[test]
69    fn never_has_no_ansi() {
70        let out = kv(ColorMode::Never, [("release", "v0.0.1")]);
71        assert!(!out.contains('\u{1b}'), "{out:?}");
72        assert!(out.contains("release"), "{out}");
73    }
74
75    #[test]
76    fn grid_has_headers() {
77        let out = grid(
78            ColorMode::Never,
79            &["id", "runner"],
80            [vec!["linux-x64".into(), "ubuntu-latest".into()]],
81        );
82        assert!(out.contains("linux-x64"), "{out}");
83        assert!(out.contains("ubuntu-latest"), "{out}");
84        assert!(out.contains("id"), "{out}");
85    }
86}