jsonl/
lib.rs

1#![warn(rust_2018_idioms, missing_debug_implementations)]
2
3//! An implementation of JSON Lines for Rust.
4//!
5//! [JSON Lines](https://jsonlines.org) is a simple format consisting of [JSON](https://json.org)
6//! values separated by newlines. Use [`read()`] and [`write()`] to interact wtih readers and
7//! writers in the JSON Lines format. Serialization and deserialization is done automatically.
8//!
9//! See [`Connection`] for situations in which you have both a reader and a writer and would like to
10//! bundle them up together.
11//!
12//! Enable the `tokio` feature to replace the usages of `std` IO primitives with those from Tokio.
13
14mod connection;
15mod errors;
16
17pub use connection::Connection;
18pub use errors::{ReadError, WriteError};
19
20#[cfg(not(feature = "tokio"))]
21mod imp {
22    use super::*;
23    use std::io::{BufRead, Write};
24
25    /// Reads a line from the reader and deserializes it into a given type.
26    pub fn read<R: BufRead, T: serde::de::DeserializeOwned>(mut reader: R) -> Result<T, ReadError> {
27        let mut buf = String::new();
28        let num_bytes_read = reader.read_line(&mut buf).map_err(ReadError::Io)?;
29
30        if num_bytes_read == 0 {
31            return Err(ReadError::Eof);
32        }
33
34        Ok(serde_json::from_str(&buf).map_err(ReadError::Deserialize)?)
35    }
36
37    /// Writes a given value to the writer, serializing it into JSON.
38    pub fn write<W: Write, T: serde::Serialize>(mut writer: W, t: &T) -> Result<(), WriteError> {
39        // We use to_string here instead of to_vec because it verifies that the JSON is valid UTF-8,
40        // which is required by the JSON Lines specification (https://jsonlines.org).
41        let json = serde_json::to_string(t).map_err(WriteError::Serialize)?;
42
43        writer.write_all(json.as_bytes()).map_err(WriteError::Io)?;
44        writer.write_all(b"\n").map_err(WriteError::Io)?;
45
46        Ok(())
47    }
48}
49
50#[cfg(feature = "tokio")]
51mod imp {
52    use super::*;
53    use tokio::io::{AsyncBufRead as BufRead, AsyncBufReadExt, AsyncWrite as Write, AsyncWriteExt};
54
55    /// Reads a line from the reader and deserializes it into a given type.
56    pub async fn read<R: BufRead + Unpin, T: serde::de::DeserializeOwned>(
57        mut reader: R,
58    ) -> Result<T, ReadError> {
59        let mut buf = String::new();
60        let num_bytes_read = reader.read_line(&mut buf).await.map_err(ReadError::Io)?;
61
62        if num_bytes_read == 0 {
63            return Err(ReadError::Eof);
64        }
65
66        Ok(serde_json::from_str(&buf).map_err(ReadError::Deserialize)?)
67    }
68
69    /// Writes a given value to the writer, serializing it into JSON.
70    pub async fn write<W: Write + Unpin, T: serde::Serialize>(
71        mut writer: W,
72        t: &T,
73    ) -> Result<(), WriteError> {
74        // We use to_string here instead of to_vec because it verifies that the JSON is valid UTF-8,
75        // which is required by the JSON Lines specification (https://jsonlines.org).
76        let json = serde_json::to_string(t).map_err(WriteError::Serialize)?;
77
78        writer
79            .write_all(json.as_bytes())
80            .await
81            .map_err(WriteError::Io)?;
82
83        writer.write_all(b"\n").await.map_err(WriteError::Io)?;
84
85        Ok(())
86    }
87}
88
89pub use imp::*;