diff --git a/chorus-lib/src/types/parse/json_parse.rs b/chorus-lib/src/types/parse/json_parse.rs index cfb0674..eb60c0d 100644 --- a/chorus-lib/src/types/parse/json_parse.rs +++ b/chorus-lib/src/types/parse/json_parse.rs @@ -118,7 +118,6 @@ pub fn read_kind(input: &[u8], inposp: &mut usize) -> Result { } } -// HELP written for event only // From the outer bracket through to the character after the close outer bracket // returns the size of data written to the output pub fn read_tags_array( @@ -234,10 +233,19 @@ pub fn read_tag( output: &mut [u8], outposp: &mut usize, ) -> Result<(), Error> { - verify_char(input, b'"', inposp)?; - let countpos = *outposp; *outposp += 2; + + // handle empty tag + if input[*inposp] == b']' { + *inposp += 1; + put(output, countpos, 0_u16.to_ne_bytes().as_slice())?; + + return Ok(()); + } + + verify_char(input, b'"', inposp)?; + let mut num_strings: usize = 1; loop { // read string @@ -336,7 +344,11 @@ pub fn burn_string(input: &[u8], inposp: &mut usize) -> Result<(), Error> { // ending on the character following the close brace pub fn burn_tag(input: &[u8], inposp: &mut usize) -> Result<(), Error> { eat_whitespace(input, inposp); - // assuming that every tag must have at least one string + // handle empty tag + if input[*inposp] == b']' { + *inposp += 1; + return Ok(()); + } verify_char(input, b'"', inposp)?; burn_string(input, inposp)?; eat_whitespace(input, inposp); diff --git a/chorus-lib/src/types/tags.rs b/chorus-lib/src/types/tags.rs index bb8f94b..2e98c89 100644 --- a/chorus-lib/src/types/tags.rs +++ b/chorus-lib/src/types/tags.rs @@ -1,4 +1,5 @@ use crate::error::{ChorusError, Error}; +use crate::types::parse::json_parse::read_tags_array; use std::fmt; /* @@ -32,6 +33,15 @@ impl<'a> Tags<'a> { Ok(Tags(&input[0..len])) } + /// Parse JSON input into a Tags. + /// + /// Returns the count of consumed input bytes and the Tags + pub fn from_json(json: &[u8], output_buffer: &'a mut [u8]) -> Result<(usize, Tags<'a>), Error> { + let mut inpos: usize = 0; + let tags_size = read_tags_array(json, &mut inpos, output_buffer)?; + Ok((inpos, Tags(&output_buffer[..tags_size]))) + } + // This copies pub fn copy(&self, output: &mut [u8]) -> Result<(), Error> { if output.len() < self.0.len() { @@ -284,4 +294,39 @@ mod test { r#"[["Hello world!","Hello","world!"],["p","ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49"]]"# ); } + + #[test] + fn test_tags_from_json() { + let mut output: Vec = Vec::with_capacity(4096); + output.resize(4096, 0); + + let json = r#"[["Hello world!","Hello","world!"],["p","ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49"]]"#; + + let (_, tags) = Tags::from_json(json.as_bytes(), &mut output).unwrap(); + + assert_eq!(tags.get_string(0, 0), Some(b"Hello world!".as_slice())); + assert_eq!(tags.get_string(0, 1), Some(b"Hello".as_slice())); + assert_eq!(tags.get_string(0, 2), Some(b"world!".as_slice())); + assert_eq!(tags.get_string(0, 3), None); + assert_eq!(tags.get_string(1, 0), Some(b"p".as_slice())); + assert_eq!( + tags.get_string(1, 1), + Some(b"ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49".as_slice()) + ); + assert_eq!(tags.get_string(1, 2), None); + assert_eq!(tags.get_string(2, 0), None); + } + + #[test] + fn test_empty_tag() { + let mut output: Vec = Vec::with_capacity(256); + output.resize(256, 0); + + let json = r#"[[]]"#; + let (_, _tags) = Tags::from_json(json.as_bytes(), &mut output).unwrap(); + + let json = r#"[["-"],[]]"#; + let (_, tags) = Tags::from_json(json.as_bytes(), &mut output).unwrap(); + assert_eq!(tags.get_string(0, 0), Some(b"-".as_slice())); + } }