1 use crate::*;
2 
3 #[derive(Debug, Clone)]
4 pub struct CellDef {
5     /// how the cell will be filled, may be a template
6     pub md: String,
7 
8     pub align: Alignment,
9 }
10 
11 #[derive(Debug, Clone)]
12 pub struct Col {
13     pub header: CellDef,
14     pub content: CellDef,
15 }
16 
17 /// A facility to build templates for tables
18 ///
19 /// You can for example build a table this two ways:
20 ///
21 /// ### with an explicit string:
22 ///
23 /// ```
24 /// static MD: &str = r#"
25 /// |-:|:-:|:-:|:-:|:-:|-:|:-:|:-:|:-|:-
26 /// |id|dev|filesystem|disk|type|used|use%|free|size|mount point
27 /// |-:|:-|:-|:-:|:-:|-:|-:|-:|:-
28 /// ${rows
29 /// |${id}|${dev-major}:${dev-minor}|${filesystem}|${disk}|${type}|~~${used}~~|~~${use-percents}~~ `${bar}`|*${free}*|**${size}**|${mount-point}
30 /// }
31 /// |-:
32 /// "#;
33 /// ```
34 /// ### with a table_builder:
35 ///
36 /// ```
37 ///  use minimad::{*, Alignment::*};
38 ///
39 ///  let mut tbl = TableBuilder::default();
40 ///  tbl
41 ///      .col(Col::simple("id").align(Right))
42 ///      .col(Col::new("dev", "${dev-major}:${dev-minor}"))
43 ///      .col(Col::simple("filesystem"))
44 ///      .col(Col::simple("disk").align_content(Center))
45 ///      .col(Col::simple("type"))
46 ///      .col(Col::new("used", "~~${used}~~"))
47 ///      .col(Col::new("use%", "~~${use-percents}~~ `${bar}`").align_content(Right))
48 ///      .col(Col::new("free", "*${free}*").align(Right))
49 ///      .col(Col::new("size", "**${size}**"))
50 ///      .col(Col::simple("mount point").align(Left));
51 /// ```
52 ///
53 /// Both ways are mostly equivalent but a table builder makes it easier to dynamically
54 /// define the columns.
55 ///
56 /// (example taken from [lfs](https://github.com/Canop/lfs))
57 #[derive(Debug, Clone, Default)]
58 pub struct TableBuilder {
59     pub cols: Vec<Col>,
60     /// an optional name for the sub template for the rows.
61     /// This is mostly useful when you want to concatenate
62     /// table templates and you need to distinguish their
63     /// subs
64     pub rows_sub_name: Option<String>,
65 }
66 
67 impl CellDef {
new<S: Into<String>>(md: S) -> Self68     pub fn new<S: Into<String>>(md: S) -> Self {
69         Self {
70             md: md.into(),
71             align: Alignment::Unspecified,
72         }
73     }
align(mut self, align: Alignment) -> Self74     pub fn align(mut self, align: Alignment) -> Self {
75         self.align = align;
76         self
77     }
78 }
79 
80 impl Col {
simple<S: AsRef<str>>(var_name: S) -> Self81     pub fn simple<S: AsRef<str>>(var_name: S) -> Self {
82         Self::new(
83             var_name.as_ref().to_string(),
84             format!("${{{}}}", var_name.as_ref().replace(' ', "-")),
85         )
86     }
new<H: Into<String>, C: Into<String>>(header_md: H, content_md: C) -> Self87     pub fn new<H: Into<String>, C: Into<String>>(header_md: H, content_md: C) -> Self {
88         Self {
89             header: CellDef::new(header_md).align(Alignment::Center),
90             content: CellDef::new(content_md),
91         }
92     }
align(mut self, align: Alignment) -> Self93     pub fn align(mut self, align: Alignment) -> Self {
94         self.header.align = align;
95         self.content.align = align;
96         self
97     }
align_header(mut self, align: Alignment) -> Self98     pub fn align_header(mut self, align: Alignment) -> Self {
99         self.header.align = align;
100         self
101     }
align_content(mut self, align: Alignment) -> Self102     pub fn align_content(mut self, align: Alignment) -> Self {
103         self.content.align = align;
104         self
105     }
106 }
107 
108 impl TableBuilder {
col(&mut self, col: Col) -> &mut Self109     pub fn col(&mut self, col: Col) -> &mut Self {
110         self.cols.push(col);
111         self
112     }
113     /// build the string of a template of the table
template_md(&self) -> String114     pub fn template_md(&self) -> String {
115         let mut md = String::new();
116         for col in &self.cols {
117             md.push_str(col.header.align.col_spec());
118         }
119         md.push('\n');
120         for col in &self.cols {
121             md.push('|');
122             md.push_str(&col.header.md);
123         }
124         md.push('\n');
125         for col in &self.cols {
126             md.push_str(col.content.align.col_spec());
127         }
128         md.push_str("\n${");
129         if let Some(name) = self.rows_sub_name.as_ref() {
130             md.push_str(name);
131         } else {
132             md.push_str("rows");
133         }
134         md.push('\n');
135         for col in &self.cols {
136             md.push('|');
137             md.push_str(&col.content.md);
138         }
139         md.push_str("\n}\n|-\n");
140         md
141     }
142 }
143 
144 
145 impl From<&TableBuilder> for String {
from(tblbl: &TableBuilder) -> String146     fn from(tblbl: &TableBuilder) -> String {
147         tblbl.template_md()
148     }
149 }
150