aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/crates/cli/src/candidates.rs4
-rw-r--r--src/crates/cli/src/display.rs146
-rw-r--r--src/crates/cli/src/estimate.rs8
-rw-r--r--src/crates/cli/src/main.rs6
-rw-r--r--src/crates/cli/src/profile.rs4
5 files changed, 111 insertions, 57 deletions
diff --git a/src/crates/cli/src/candidates.rs b/src/crates/cli/src/candidates.rs
index 6d76da3..e8171e3 100644
--- a/src/crates/cli/src/candidates.rs
+++ b/src/crates/cli/src/candidates.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use argh::FromArgs;
use log::info;
-use crate::{display::print_table, State};
+use crate::State;
/// List selection sites and their candidates
#[derive(FromArgs)]
@@ -16,7 +16,7 @@ impl State {
let candidates = self.inner.project_candidate_list(proj)?;
for (file, con_types) in candidates.iter_2d() {
- print_table(con_types.flat_map(|(ctn, candidates)| {
+ self.print_table(con_types.flat_map(|(ctn, candidates)| {
candidates.iter().map(move |v| {
(
ctn,
diff --git a/src/crates/cli/src/display.rs b/src/crates/cli/src/display.rs
index b48e370..38e5c44 100644
--- a/src/crates/cli/src/display.rs
+++ b/src/crates/cli/src/display.rs
@@ -1,57 +1,105 @@
use std::{collections::HashSet, fmt::Display, hash::Hash, iter::once};
use candelabra::profiler::UsageProfile;
-use tabled::{builder::Builder, settings::Style};
-
-// Print the given 2D map as a table, where the first key is the left-most column, and the second key the column index
-pub fn print_table<I1, I2, A, B, C>(map: I1)
-where
- I1: IntoIterator<Item = (A, I2)>,
- I2: IntoIterator<Item = (B, C)>,
- A: Display,
- B: Display + Eq + Hash,
- C: Display,
-{
- // Collect everything up
- let map = map
- .into_iter()
- .map(|(k, v)| (k, v.into_iter().collect::<Vec<(_, _)>>()))
- .collect::<Vec<(_, _)>>();
-
- // Get keys
- let keys = map
- .iter()
- .flat_map(|(_, vs)| vs.iter().map(|(k, _)| k))
- .collect::<HashSet<_>>() // dedup
- .into_iter()
- .collect::<Vec<_>>(); // consistent order
-
- let mut builder = Builder::new();
- builder.set_header(once("".to_string()).chain(keys.iter().map(|s| s.to_string())));
-
- // Add rows
- for (key, vals) in map.iter() {
- builder.push_record(once(key.to_string()).chain(keys.iter().map(|k| {
- vals.iter()
- .find(|(search, _)| search == *k)
- .map(|(_, v)| v.to_string())
- .unwrap_or("".to_string())
- })));
+use tabled::{builder::Builder, grid::records::vec_records::Cell, settings::Style, Table};
+
+use crate::State;
+
+impl State {
+ // Print the given 2D map as a table, where the first key is the left-most column, and the second key the column index
+ pub fn print_table<I1, I2, A, B, C>(&self, map: I1)
+ where
+ I1: IntoIterator<Item = (A, I2)>,
+ I2: IntoIterator<Item = (B, C)>,
+ A: Display,
+ B: Display + Eq + Hash,
+ C: Display,
+ {
+ // Collect everything up
+ let map = map
+ .into_iter()
+ .map(|(k, v)| (k, v.into_iter().collect::<Vec<(_, _)>>()))
+ .collect::<Vec<(_, _)>>();
+
+ // Get keys
+ let keys = map
+ .iter()
+ .flat_map(|(_, vs)| vs.iter().map(|(k, _)| k))
+ .collect::<HashSet<_>>() // dedup
+ .into_iter()
+ .collect::<Vec<_>>(); // consistent order
+
+ let mut builder = Builder::new();
+ builder.set_header(once("".to_string()).chain(keys.iter().map(|s| s.to_string())));
+
+ // Add rows
+ for (key, vals) in map.iter() {
+ builder.push_record(once(key.to_string()).chain(keys.iter().map(|k| {
+ vals.iter()
+ .find(|(search, _)| search == *k)
+ .map(|(_, v)| v.to_string())
+ .unwrap_or("".to_string())
+ })));
+ }
+
+ let mut table = builder.build();
+ println!("{}", table.with(Style::sharp()));
+
+ if self.with_latex {
+ print_latex(table);
+ }
}
- println!("{}", builder.build().with(Style::sharp()));
+ pub fn display_usage_info(&self, profile_info: &UsageProfile) {
+ self.print_table(profile_info.0.iter().enumerate().map(|(i, p)| {
+ (
+ i,
+ [
+ ("n".to_string(), p.avg_n),
+ ("occurences".to_string(), p.occurences),
+ ]
+ .into_iter()
+ .chain(p.avg_op_counts.clone()),
+ )
+ }))
+ }
}
-pub fn display_usage_info(profile_info: &UsageProfile) {
- print_table(profile_info.0.iter().enumerate().map(|(i, p)| {
- (
- i,
- [
- ("n".to_string(), p.avg_n),
- ("occurences".to_string(), p.occurences),
- ]
- .into_iter()
- .chain(p.avg_op_counts.clone()),
- )
- }))
+fn print_latex(t: Table) {
+ println!(
+ "\\begin{{center}}\n\\begin{{tabular}}{{{}|}}",
+ "|c".repeat(t.shape().1)
+ );
+ for (i, row) in t.get_records().iter().enumerate() {
+ if row.is_empty() {
+ continue;
+ }
+
+ print!("{} ", escape_latex(row[0].text()));
+ for col in &row[1..] {
+ print!("& {} ", escape_latex(col.text()));
+ }
+ println!("\\\\");
+
+ // header line
+ if i == 0 {
+ println!("\\hline");
+ }
+ }
+ println!("\\end{{tabular}}\n\\end{{center}}");
+}
+
+fn escape_latex(s: &str) -> String {
+ let mut out = String::with_capacity(s.len());
+ for c in s.chars() {
+ match c {
+ '\\' | '{' | '}' | '_' | '^' | '#' | '&' | '$' | '%' | '~' => {
+ out.push('\\');
+ out.push(c);
+ }
+ c => out.push(c),
+ }
+ }
+
+ out
}
diff --git a/src/crates/cli/src/estimate.rs b/src/crates/cli/src/estimate.rs
index be5dc5c..0ba0615 100644
--- a/src/crates/cli/src/estimate.rs
+++ b/src/crates/cli/src/estimate.rs
@@ -6,7 +6,7 @@ use candelabra::{select::Selection, types::Mapping2D};
use cargo_metadata::camino::Utf8PathBuf;
use log::info;
-use crate::{display::print_table, State};
+use crate::State;
/// Estimate the cost of a given set of assignments for a specific project, and detail how that was reached.
#[derive(FromArgs)]
@@ -76,7 +76,7 @@ impl State {
}
}
- print_table(acc.into_iter_2d());
+ self.print_table(acc.into_iter_2d());
info!("Breakdown by split");
profile_info
@@ -93,7 +93,7 @@ impl State {
})
.for_each(|(f, ctn, table)| {
info!("{} / {}:", f, ctn);
- print_table(table);
+ self.print_table(table);
});
if !args.run {
@@ -102,7 +102,7 @@ impl State {
info!("Running benchmarks for comparison");
- print_table(
+ self.print_table(
self.inner
.run_benchmarks_with(
proj,
diff --git a/src/crates/cli/src/main.rs b/src/crates/cli/src/main.rs
index 04bcc96..552e5a9 100644
--- a/src/crates/cli/src/main.rs
+++ b/src/crates/cli/src/main.rs
@@ -22,6 +22,10 @@ pub struct Args {
#[argh(option, short = 'p')]
pub project: Option<String>,
+ /// also output LaTeX tables
+ #[argh(switch, short = 'l')]
+ pub with_latex: bool,
+
#[argh(subcommand)]
pub cmd: Subcommand,
}
@@ -47,6 +51,7 @@ fn main() -> Result<()> {
let state = State {
inner: candelabra::State::new(paths)?,
projects: get_projects(&args)?,
+ with_latex: args.with_latex,
};
match args.cmd {
@@ -62,6 +67,7 @@ fn main() -> Result<()> {
struct State {
inner: candelabra::State,
projects: Vec<Project>,
+ with_latex: bool,
}
fn get_projects(args: &Args) -> Result<Vec<Project>> {
diff --git a/src/crates/cli/src/profile.rs b/src/crates/cli/src/profile.rs
index af437ba..27a16a1 100644
--- a/src/crates/cli/src/profile.rs
+++ b/src/crates/cli/src/profile.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use argh::FromArgs;
use log::info;
-use crate::{display::display_usage_info, State};
+use crate::State;
/// Profile the selected projects and print the results
#[derive(FromArgs)]
@@ -18,7 +18,7 @@ impl State {
for (f, ctn, usage_profile) in all_info.iter() {
info!("{} / {}:", f, ctn);
- display_usage_info(usage_profile);
+ self.display_usage_info(usage_profile);
}
}
Ok(())