Skip to main content

pito_compare/
lib.rs

1pub mod canvas;
2pub mod chart;
3pub mod draw;
4
5pub use canvas::{Canvas, Color};
6pub use chart::{Better, Chart, Format, Group, Party, Row, Words};
7pub use draw::{bars, paired, ratios};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct Error(String);
11
12impl Error {
13    pub fn new(message: impl Into<String>) -> Self {
14        Self(message.into())
15    }
16
17    pub fn message(&self) -> &str {
18        &self.0
19    }
20}
21
22impl std::fmt::Display for Error {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.write_str(&self.0)
25    }
26}
27
28impl std::error::Error for Error {}
29
30pub type Result<T> = std::result::Result<T, Error>;
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum Kind {
34    All,
35    Paired,
36    Ratio,
37    Bars,
38}
39
40pub fn render(chart: &Chart, kind: Kind) -> Result<Vec<(String, Canvas)>> {
41    chart.check()?;
42    let mut out = Vec::new();
43    let single = chart.parties.len() == 1;
44    if kind == Kind::Paired || (kind == Kind::All && !single) {
45        out.push(("paired".to_string(), paired(chart)?));
46    }
47    if kind == Kind::Ratio || (kind == Kind::All && !single) {
48        out.push(("ratio".to_string(), ratios(chart)?));
49    }
50    if kind == Kind::Bars || (kind == Kind::All && single) {
51        if single {
52            out.push(("bars".to_string(), bars(chart, 0)?));
53        } else {
54            let mut used = Vec::new();
55            for (i, p) in chart.parties.iter().enumerate() {
56                let mut name = format!("bars-{}", slug(&p.name));
57                if name == "bars-" || used.contains(&name) {
58                    name = format!("bars-{}", i + 1);
59                }
60                used.push(name.clone());
61                out.push((name, bars(chart, i)?));
62            }
63        }
64    }
65    Ok(out)
66}
67
68fn slug(name: &str) -> String {
69    let mut s = String::new();
70    for c in name.chars() {
71        if c.is_ascii_alphanumeric() {
72            s.push(c.to_ascii_lowercase());
73        } else if !s.ends_with('-') && !s.is_empty() {
74            s.push('-');
75        }
76    }
77    s.trim_end_matches('-').to_string()
78}