X-Git-Url: http://git.scottworley.com/tablify/blobdiff_plain/06a6a5cad195d0b7504c4e7b4caf085487f53578..12e913005080a9f60750d807c220a58704429361:/src/lib.rs?ds=inline diff --git a/src/lib.rs b/src/lib.rs index 73e517b..dbcfc7b 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,8 @@ impl>> Iterator for Reader } } -fn read_rows(input: impl std::io::Read) -> impl Iterator> { - Reader::new(std::io::BufReader::new(input).lines()) +fn read_rows(input: impl std::io::Read) -> Result, std::io::Error> { + Reader::new(std::io::BufReader::new(input).lines()).collect::, _>>() } fn column_counts(rows: &[Rowlike]) -> Vec<(usize, String)> { @@ -265,7 +266,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 @@ -303,7 +304,7 @@ fn render_column_headers(columns: &[String]) -> HTML { /// * 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 rows = read_rows(input)?; let columns = column_order(config, &rows); Ok(HTML(format!( "{HEADER}{}{}{FOOTER}", @@ -350,21 +351,21 @@ mod tests { #[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 +378,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 +395,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 +408,13 @@ mod tests { ] ); assert_eq!( - read_rows(&b"foo\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,20 +422,31 @@ mod tests { ] ); assert_eq!( - read_rows(&b"foo \n bar \n"[..]) - .flatten() - .collect::>(), + read_rows(&b"foo\n \nbar\n"[..]).unwrap(), + vec![ + Rowlike::Row(Row { + label: "foo".to_owned(), + entries: HashMap::new(), + }), + Rowlike::Row(Row { + label: "bar".to_owned(), + entries: HashMap::new(), + }) + ] + ); + assert_eq!( + 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")); } @@ -447,34 +454,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"))] );