X-Git-Url: http://git.scottworley.com/tablify/blobdiff_plain/722ea29747d6e820490db0bf988cda86c3341565..166a543d8299317183ec9226c3af174a6d4f4fc9:/src/lib.rs?ds=sidebyside diff --git a/src/lib.rs b/src/lib.rs index e11f08b..5c994c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ const HEADER: &str = r#" /* h/t https://wabain.github.io/2019/10/13/css-rotated-table-header.html */ th, td { white-space: nowrap; } th { text-align: left; font-weight: normal; } + th.spacer_row { height: .3em; } table { border-collapse: collapse } tr.key > th { height: 10em; vertical-align: bottom; line-height: 1 } tr.key > th > div { width: 1em; } @@ -138,7 +139,7 @@ impl>> Iterator for Reader InputLine::Blank if self.row.is_some() => { return Ok(std::mem::take(&mut self.row).map(Rowlike::Row)).transpose() } - InputLine::Blank => {} + InputLine::Blank => return Some(Ok(Rowlike::Spacer)), InputLine::Entry(col, instance) => match &mut self.row { None => { return Some(Err(std::io::Error::other(format!( @@ -169,8 +170,13 @@ impl>> Iterator for Reader } } -fn read_rows(input: impl std::io::Read) -> impl Iterator> { +fn read_input(input: impl std::io::Read) -> Result<(Vec, Config), std::io::Error> { + let default_config = Config { + column_threshold: 2, + }; Reader::new(std::io::BufReader::new(input).lines()) + .collect::, _>>() + .map(|rows| (rows, default_config)) } fn column_counts(rows: &[Rowlike]) -> Vec<(usize, String)> { @@ -265,7 +271,7 @@ fn render_all_leftovers(row: &Row) -> HTML { fn render_row(columns: &[String], rowlike: &mut Rowlike) -> HTML { match rowlike { - Rowlike::Spacer => HTML::from(" "), + Rowlike::Spacer => HTML::from("\n"), Rowlike::Row(row) => { let row_label = HTML::escape(row.label.as_ref()); let cells = columns @@ -302,9 +308,9 @@ fn render_column_headers(columns: &[String]) -> HTML { /// * there's an i/o error while reading `input` /// * the log has invalid syntax: /// * an indented line with no preceding non-indented line -pub fn tablify(config: &Config, input: impl std::io::Read) -> Result { - let rows = read_rows(input).collect::, _>>()?; - let columns = column_order(config, &rows); +pub fn tablify(input: impl std::io::Read) -> Result { + let (rows, config) = read_input(input)?; + let columns = column_order(&config, &rows); Ok(HTML(format!( "{HEADER}{}{}{FOOTER}", render_column_headers(&columns), @@ -347,24 +353,27 @@ mod tests { ); } + fn read_rows(input: impl std::io::Read) -> Result, std::io::Error> { + read_input(input).map(|(rows, _)| rows) + } #[test] fn test_read_rows() { assert_eq!( - read_rows(&b"foo"[..]).flatten().collect::>(), + read_rows(&b"foo"[..]).unwrap(), vec![Rowlike::Row(Row { label: "foo".to_owned(), entries: HashMap::new(), })] ); assert_eq!( - read_rows(&b"bar"[..]).flatten().collect::>(), + read_rows(&b"bar"[..]).unwrap(), vec![Rowlike::Row(Row { label: "bar".to_owned(), entries: HashMap::new(), })] ); assert_eq!( - read_rows(&b"foo\nbar\n"[..]).flatten().collect::>(), + read_rows(&b"foo\nbar\n"[..]).unwrap(), vec![ Rowlike::Row(Row { label: "foo".to_owned(), @@ -377,16 +386,14 @@ mod tests { ] ); assert_eq!( - read_rows(&b"foo\n bar\n"[..]).flatten().collect::>(), + read_rows(&b"foo\n bar\n"[..]).unwrap(), vec![Rowlike::Row(Row { label: "foo".to_owned(), entries: HashMap::from([("bar".to_owned(), vec![None])]), })] ); assert_eq!( - read_rows(&b"foo\n bar\n baz\n"[..]) - .flatten() - .collect::>(), + read_rows(&b"foo\n bar\n baz\n"[..]).unwrap(), vec![Rowlike::Row(Row { label: "foo".to_owned(), entries: HashMap::from([ @@ -396,9 +403,7 @@ mod tests { })] ); assert_eq!( - read_rows(&b"foo\n\nbar\n"[..]) - .flatten() - .collect::>(), + read_rows(&b"foo\n\nbar\n"[..]).unwrap(), vec![ Rowlike::Row(Row { label: "foo".to_owned(), @@ -411,14 +416,13 @@ mod tests { ] ); assert_eq!( - read_rows(&b"foo\n\n\nbar\n"[..]) - .flatten() - .collect::>(), + read_rows(&b"foo\n\n\nbar\n"[..]).unwrap(), vec![ Rowlike::Row(Row { label: "foo".to_owned(), entries: HashMap::new(), }), + Rowlike::Spacer, Rowlike::Row(Row { label: "bar".to_owned(), entries: HashMap::new(), @@ -426,9 +430,7 @@ mod tests { ] ); assert_eq!( - read_rows(&b"foo\n \nbar\n"[..]) - .flatten() - .collect::>(), + read_rows(&b"foo\n \nbar\n"[..]).unwrap(), vec![ Rowlike::Row(Row { label: "foo".to_owned(), @@ -441,20 +443,18 @@ mod tests { ] ); assert_eq!( - read_rows(&b"foo \n bar \n"[..]) - .flatten() - .collect::>(), + read_rows(&b"foo \n bar \n"[..]).unwrap(), vec![Rowlike::Row(Row { label: "foo".to_owned(), entries: HashMap::from([("bar".to_owned(), vec![None])]), })] ); - let bad = read_rows(&b" foo"[..]).next().unwrap(); + let bad = read_rows(&b" foo"[..]); assert!(bad.is_err()); assert!(format!("{bad:?}").contains("1: Entry with no header")); - let bad2 = read_rows(&b"foo\n\n bar"[..]).nth(1).unwrap(); + let bad2 = read_rows(&b"foo\n\n bar"[..]); assert!(bad2.is_err()); assert!(format!("{bad2:?}").contains("3: Entry with no header")); } @@ -462,34 +462,20 @@ mod tests { #[test] fn test_column_counts() { assert_eq!( - column_counts( - &read_rows(&b"foo\n bar\n baz\n"[..]) - .collect::, _>>() - .unwrap() - ), + column_counts(&read_rows(&b"foo\n bar\n baz\n"[..]).unwrap()), vec![(1, String::from("bar")), (1, String::from("baz"))] ); assert_eq!( - column_counts( - &read_rows(&b"foo\n bar\n baz\nquux\n baz"[..]) - .collect::, _>>() - .unwrap() - ), + column_counts(&read_rows(&b"foo\n bar\n baz\nquux\n baz"[..]).unwrap()), vec![(2, String::from("baz")), (1, String::from("bar"))] ); assert_eq!( - column_counts( - &read_rows(&b"foo\n bar\n bar\n baz\n bar\nquux\n baz"[..]) - .collect::, _>>() - .unwrap() - ), + column_counts(&read_rows(&b"foo\n bar\n bar\n baz\n bar\nquux\n baz"[..]).unwrap()), vec![(2, String::from("baz")), (1, String::from("bar"))] ); assert_eq!( column_counts( - &read_rows(&b"foo\n bar: 1\n bar: 2\n baz\n bar\nquux\n baz"[..]) - .collect::, _>>() - .unwrap() + &read_rows(&b"foo\n bar: 1\n bar: 2\n baz\n bar\nquux\n baz"[..]).unwrap() ), vec![(2, String::from("baz")), (1, String::from("bar"))] );