Skip to main content

table_editor/
jsonl.rs

1//! Reading and writing the one-row-per-line JSON the tables are stored as.
2//!
3//! Writing is not a rewrite of what was read. Blank lines are skipped on the
4//! way in and never written on the way out, and each row is re-serialized from
5//! its parsed form, so spacing within a line and the order of a row's keys
6//! follow the row type rather than the file. Every row ends in a newline,
7//! including the last.
8
9use serde::Serialize;
10use serde::de::DeserializeOwned;
11
12use crate::error::ParseError;
13
14/// Deserialize one row per non-blank line. The first line that does not
15/// deserialize stops the parse and is reported by its one-based number.
16pub fn parse<T: DeserializeOwned>(text: &str) -> Result<Vec<T>, ParseError> {
17    let mut rows = Vec::new();
18    for (idx, raw) in text.lines().enumerate() {
19        let line = idx + 1;
20        let trimmed = raw.trim();
21        if trimmed.is_empty() {
22            continue;
23        }
24        let row: T = serde_json::from_str(trimmed).map_err(|e| ParseError {
25            line,
26            message: e.to_string(),
27        })?;
28        rows.push(row);
29    }
30    Ok(rows)
31}
32
33/// Serialize one row per line, each terminated by a newline.
34pub fn serialize<T: Serialize>(rows: &[T]) -> Result<String, serde_json::Error> {
35    let mut out = String::new();
36    for row in rows {
37        out.push_str(&serde_json::to_string(row)?);
38        out.push('\n');
39    }
40    Ok(out)
41}
42
43#[cfg(test)]
44mod tests {
45    use serde::Deserialize;
46
47    use super::*;
48
49    #[derive(Debug, PartialEq, Serialize, Deserialize)]
50    struct Row {
51        name: String,
52    }
53
54    #[test]
55    fn blank_lines_are_skipped() {
56        let rows: Vec<Row> = parse("{\"name\":\"a\"}\n\n  \n{\"name\":\"b\"}\n").unwrap();
57        assert_eq!(rows.len(), 2);
58        assert_eq!(rows[1].name, "b");
59    }
60
61    #[test]
62    fn parse_reports_the_offending_line() {
63        let err = parse::<Row>("{\"name\":\"a\"}\nnot json\n").unwrap_err();
64        assert_eq!(err.line, 2);
65    }
66
67    #[test]
68    fn serialize_terminates_every_row() {
69        let text = serialize(&[Row { name: "a".into() }, Row { name: "b".into() }]).unwrap();
70        assert_eq!(text, "{\"name\":\"a\"}\n{\"name\":\"b\"}\n");
71    }
72
73    #[test]
74    fn empty_rows_serialize_to_empty_text() {
75        assert_eq!(serialize::<Row>(&[]).unwrap(), "");
76    }
77}