use itertools::Itertools;
use std::fmt::Display;
pub trait TeX {
fn tex_math_symbol() -> String;
fn tex_string(&self) -> String;
}
pub fn tex_table<S, I, J, I1, I2, D, F>(caption: &str, head: S, rows: I1, cols: I2, entry: F, math_mode: bool, hor_at_top: bool) -> String
where
S: Display,
I: Display,
J: Display,
I1: IntoIterator<Item = I>,
I2: IntoIterator<Item = J>,
D: Display,
F: Fn(&I, &J) -> D
{
fn disp<S>(s: S, math_mode: bool) -> String where S: Display {
if math_mode {
let s = s.to_string();
if s.is_empty() {
"$ $".to_string()
} else {
format!("${}$", s)
}
} else {
s.to_string()
}
}
let cols = cols.into_iter().collect_vec();
let mut res = String::new();
res += r#"\begin{table}
\centering
\begin{tabular}"#;
res += &format!("{{r|{}}}\n", "l".repeat(cols.len()));
let row = |head: String, cells: Vec<String>|
std::iter::once(head).chain(cells).join(" & ") + " \\\\\n";
let hor = row(
disp(head, math_mode),
cols.iter().map(|c| disp(c, math_mode)).collect_vec()
);
if hor_at_top {
res += &hor;
res += "\\hline\n";
}
for i in rows {
res += &row(
disp(&i, math_mode),
cols.iter().map(|j| disp(entry(&i, j), math_mode)).collect_vec()
);
}
if !hor_at_top {
res += "\\hline\n";
res += &hor;
}
res += "\\end{tabular}\n";
res += &format!("\\caption{{{caption}}}\n");
res += "\\end{table}\n";
res
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tex_table() {
let _table = tex_table("Caption", "i, j", [1, 2, 3], [4, 5, 6, 7], |i, j| i * 10 + j, true, false);
}
#[test]
fn table_without_columns() {
let entry = |i: &i32, j: &i32| i * 10 + j;
let none: [i32; 0] = [];
let t = tex_table("Caption", "i", none, none, entry, true, true);
assert!(t.contains("{r|}"));
assert!(!t.contains('&'), "empty table has a stray `&`:\n{t}");
let t = tex_table("Caption", "i", [1, 2], none, entry, true, true);
assert!(!t.contains('&'), "column-less rows have a stray `&`:\n{t}");
let t = tex_table("Caption", "i", [1], [4, 5], entry, true, true);
assert!(t.lines().all(|l| !l.ends_with("\\\\") || l.matches('&').count() == 2), "{t}");
}
}