summaryrefslogtreecommitdiff
path: root/src/utils.rs
diff options
context:
space:
mode:
authorMax Verevkin <maxxverrr@gmail.com>2022-04-04 16:53:49 +0300
committerMax Verevkin <maxxverrr@gmail.com>2022-04-04 16:53:49 +0300
commitb759c7ec2ac5411f7dd254bb19a20c27a166e7e2 (patch)
tree750b9143ed9af2c8582a5200841e18ab0598e9c5 /src/utils.rs
parent41b54360ff917a261d3f6f752e3c2c51edada773 (diff)
downloadi3bar-mango-b759c7ec2ac5411f7dd254bb19a20c27a166e7e2.tar.gz
dont use line buffering when using json protocol
Diffstat (limited to 'src/utils.rs')
-rw-r--r--src/utils.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..2ed1868
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,105 @@
+//! Some usefull functions
+
+use serde::Deserialize;
+use serde_json::{Deserializer, Error as JsonError};
+
+/// Retuns (`last_line`, `remaining`). See tests for examples.
+pub fn last_line(s: &[u8]) -> Option<(&[u8], &[u8])> {
+ let mut it = memchr::memrchr_iter(b'\n', s);
+ let last = it.next()?;
+ let rem = &s[(last + 1)..];
+ if let Some(pre_last) = it.next() {
+ Some((&s[(pre_last + 1)..last], rem))
+ } else {
+ Some((&s[..last], rem))
+ }
+}
+
+/// Deserialize the last complete object. Returns (`object`, `remaining`). See tests for examples.
+pub fn de_last_json<'a, T: Deserialize<'a>>(
+ mut s: &'a [u8],
+) -> Result<(Option<T>, &'a [u8]), JsonError> {
+ let mut last = None;
+ let mut tmp;
+ loop {
+ (tmp, s) = de_first_json(s)?;
+ last = match tmp {
+ Some(obj) => Some(obj),
+ None => return Ok((last, s)),
+ };
+ }
+}
+
+/// Deserialize the first complete object. Returns (`object`, `remaining`). See tests for examples.
+pub fn de_first_json<'a, T: Deserialize<'a>>(
+ mut s: &'a [u8],
+) -> Result<(Option<T>, &'a [u8]), JsonError> {
+ while s
+ .first()
+ .map_or(false, |&x| x == b' ' || x == b',' || x == b'\n')
+ {
+ s = &s[1..];
+ }
+ let mut de = Deserializer::from_slice(s).into_iter();
+ match de.next() {
+ Some(Ok(obj)) => Ok((Some(obj), &s[de.byte_offset()..])),
+ Some(Err(e)) if e.is_eof() => Ok((None, &s[de.byte_offset()..])),
+ Some(Err(e)) => Err(e),
+ None => Ok((None, &s[de.byte_offset()..])),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ macro_rules! str {
+ ($str:expr) => {
+ &$str.as_bytes()[..]
+ };
+ }
+
+ #[test]
+ fn streaming_json() {
+ let s = b",[2]\n, [3], [4, 3],[32][3] ";
+ assert_eq!(
+ de_first_json::<Vec<u8>>(s).unwrap(),
+ (Some(vec![2]), str!("\n, [3], [4, 3],[32][3] "))
+ );
+ assert_eq!(
+ de_last_json::<Vec<u8>>(s).unwrap(),
+ (Some(vec![3]), str!(""))
+ );
+
+ let s = b",[2]\n, [3], [4, 3],[32][3] [2, 4";
+ assert_eq!(
+ de_last_json::<Vec<u8>>(s).unwrap(),
+ (Some(vec![3]), str!("[2, 4"))
+ );
+
+ let s = b",[2]\n, [3], [4, 3],[32] invalid";
+ assert_eq!(
+ de_first_json::<Vec<u8>>(s).unwrap(),
+ (Some(vec![2]), str!("\n, [3], [4, 3],[32] invalid"))
+ );
+ assert!(de_last_json::<Vec<u8>>(s).is_err());
+ }
+
+ #[test]
+ fn test_last_line() {
+ let s = b"hello";
+ assert_eq!(last_line(s), None);
+
+ let s = b"hello\n";
+ assert_eq!(last_line(s), Some((str!("hello"), str!(""))));
+
+ let s = b"hello\nworld";
+ assert_eq!(last_line(s), Some((str!("hello"), str!("world"))));
+
+ let s = b"hello\nworld\n";
+ assert_eq!(last_line(s), Some((str!("world"), str!(""))));
+
+ let s = b"hello\nworld\n...";
+ assert_eq!(last_line(s), Some((str!("world"), str!("..."))));
+ }
+}