diff options
| author | Max Verevkin <maxxverrr@gmail.com> | 2022-04-04 16:53:49 +0300 |
|---|---|---|
| committer | Max Verevkin <maxxverrr@gmail.com> | 2022-04-04 16:53:49 +0300 |
| commit | b759c7ec2ac5411f7dd254bb19a20c27a166e7e2 (patch) | |
| tree | 750b9143ed9af2c8582a5200841e18ab0598e9c5 /src/utils.rs | |
| parent | 41b54360ff917a261d3f6f752e3c2c51edada773 (diff) | |
| download | i3bar-mango-b759c7ec2ac5411f7dd254bb19a20c27a166e7e2.tar.gz | |
dont use line buffering when using json protocol
Diffstat (limited to 'src/utils.rs')
| -rw-r--r-- | src/utils.rs | 105 |
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!("...")))); + } +} |
