diff --git a/src/lib.rs b/src/lib.rs index 4f65481..34c65e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -162,23 +162,20 @@ impl AsciiTable { self.format_inner(self.stringify(data)) } - fn format_inner(&self, data: Vec>) -> String { + fn format_inner(&self, data: Vec>) -> String { let num_cols = data.iter().map(|row| row.len()).max().unwrap_or(0); if !self.valid(&data, num_cols) { return self.format_empty() } + let header = self.stringify_header(); let data = self.square_data(data, num_cols); - let has_header = self.columns.iter().any(|(_, col)| !col.header.is_empty()); + let has_header = header.iter().any(|text| !text.is_empty()); let widths = self.column_widths(&data, num_cols); let mut result = String::new(); result.push_str(&self.format_first(&widths)); if has_header { - let default_conf = &DEFAULT_COLUMN; - let header: Vec<_> = (0..num_cols).map(|a| - self.columns.get(&a).unwrap_or(default_conf).header.as_str() - ).collect(); result.push_str(&self.format_header_row(&header, &widths)); result.push_str(&self.format_middle(&widths)); } @@ -189,7 +186,7 @@ impl AsciiTable { result } - fn valid(&self, data: &Vec>, num_cols: usize) -> bool { + fn valid(&self, data: &Vec>, num_cols: usize) -> bool { if data.len() == 0 { false } else if num_cols == 0 { @@ -205,27 +202,35 @@ impl AsciiTable { ((num_cols - 1) * 3) + 4 } - fn stringify(&self, data: L1) -> Vec> + fn stringify(&self, data: L1) -> Vec> where L1: IntoIterator, L2: IntoIterator, T: Display { - data.into_iter().map(|row| row.into_iter().map(|cell| cell.to_string()).collect()).collect() + data.into_iter().map(|row| row.into_iter().map(|cell| SmartString::from(cell)).collect()).collect() } - fn square_data(&self, mut data: Vec>, num_cols: usize) -> Vec> { + fn stringify_header(&self) -> Vec { +// let default_conf = &DEFAULT_COLUMN; +// let header: Vec<_> = (0..num_cols).map(|a| +// &self.columns.get(&a).unwrap_or(default_conf).header +// ).collect(); + todo!() + } + + fn square_data(&self, mut data: Vec>, num_cols: usize) -> Vec> { for row in data.iter_mut() { while row.len() < num_cols { - row.push(String::new()) + row.push(SmartString::new()) } } data } - fn column_widths(&self, data: &[Vec], num_cols: usize) -> Vec { + fn column_widths(&self, data: &[Vec], num_cols: usize) -> Vec { let result: Vec<_> = (0..num_cols).map(|a| { let default_conf = &DEFAULT_COLUMN; let conf = self.columns.get(&a).unwrap_or(default_conf); - let column_width = data.iter().map(|row| self.count_characters(&row[a])).max().unwrap(); + let column_width = data.iter().map(|row| row[a].char_len()).max().unwrap(); let header_width = conf.header.chars().count(); column_width.max(header_width).min(conf.max_width) }).collect(); @@ -266,7 +271,7 @@ impl AsciiTable { widths } - fn format_line(&self, row: &[String], head: &str, delim: &str, tail: &str) -> String { + fn format_line(&self, row: &[SmartString], head: &str, delim: &str, tail: &str) -> String { let mut result = String::new(); result.push_str(head); for cell in row { @@ -282,12 +287,12 @@ impl AsciiTable { fn format_empty(&self) -> String { self.format_first(&vec![0]) - + &self.format_line(&vec![String::new()], &format!("{}{}", NS, ' '), &format!("{}{}{}", ' ', NS, ' '), &format!("{}{}", ' ', NS)) - + &self.format_last(&vec![0]) + + &self.format_line(&[SmartString::new()], &format!("{}{}", NS, ' '), &format!("{}{}{}", ' ', NS, ' '), &format!("{}{}", ' ', NS)) + + &self.format_last(&[0]) } fn format_first(&self, widths: &[usize]) -> String { - let row: Vec = widths.iter().map(|&x| EW.repeat(x)).collect(); + let row: Vec = widths.iter().map(|&x| SmartString::new(EW.repeat(x))).collect(); self.format_line(&row, &format!("{}{}", SE, EW), &format!("{}{}{}", EW, EWS, EW), &format!("{}{}", EW, SW)) } @@ -296,7 +301,7 @@ impl AsciiTable { self.format_line(&row, &format!("{}{}", NES, EW), &format!("{}{}{}", EW, NEWS, EW), &format!("{}{}", EW, NWS)) } - fn format_row(&self, row: &[String], widths: &[usize]) -> String { + fn format_row(&self, row: &[SmartString], widths: &[usize]) -> String { let row: Vec<_> = (0..widths.len()).map(|a| { let cell = &row[a]; let width = widths[a]; @@ -307,7 +312,7 @@ impl AsciiTable { self.format_line(&row, &format!("{}{}", NS, ' '), &format!("{}{}{}", ' ', NS, ' '), &format!("{}{}", ' ', NS)) } - fn format_header_row(&self, row: &[&str], widths: &[usize]) -> String { + fn format_header_row(&self, row: &[SmartString], widths: &[usize]) -> String { let row: Vec = row.iter().zip(widths.iter()).map(|(cell, &width)| self.format_cell(cell, width, ' ', Align::Left) ).collect(); @@ -319,25 +324,29 @@ impl AsciiTable { self.format_line(&row, &format!("{}{}", NE, EW), &format!("{}{}{}", EW, NEW, EW), &format!("{}{}", EW, NW)) } - fn format_cell(&self, text: &str, len: usize, pad: char, align: Align) -> String { - if text.chars().count() > len { - let mut result: String = text.chars().take(len).collect(); + fn format_cell(&self, text: &SmartString, len: usize, pad: char, align: Align) -> SmartString { + if text.char_len() > len { +// let mut result: String = text.chars().take(len).collect(); + let mut result = text.clone(); + while result.char_len() > len { + result.pop(); + } if result.pop().is_some() { result.push('+') } result } else { - let mut result = text.to_string(); + let mut result = text.clone(); match align { - Align::Left => while result.chars().count() < len { + Align::Left => while result.char_len() < len { result.push(pad) } - Align::Right => while result.chars().count() < len { + Align::Right => while result.char_len() < len { result.insert(0, pad) } - Align::Center => while result.chars().count() < len { + Align::Center => while result.char_len() < len { result.push(pad); - if result.chars().count() < len { + if result.char_len() < len { result.insert(0, pad) } } @@ -346,3 +355,48 @@ impl AsciiTable { } } } + +#[derive(Clone, Debug)] +struct SmartString { + fragments: Vec<(bool, String)> +} + +impl SmartString { + + fn new() -> Self { + todo!() + } + + fn from(string: T) -> Self + where T: Display { + todo!() + } + + fn char_len(&self) -> usize { + todo!() + } + + fn is_empty(&self) -> bool { + todo!() + } + + fn pop(&mut self) -> Option { + todo!() + } + + fn push(&mut self, ch: char) { + todo!() + } + + fn insert(&mut self, index: usize, ch: char) { + todo!() + } +} + +impl Display for SmartString { + + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + let concat: String = self.fragments.iter().map(|(_, string)| string.as_str()).collect(); + concat.fmt(fmt) + } +}