Return NotEnough if nothing at current cursor

If Parser::will_cursor_give_char is set to not error if a char matches
or the next line is empty, return Ok(bool). If this_if_nothing_ahead is
set to false, then return a NotEnough error if no more chars are
available.

The newly added test explains why
next
Sayan Nandan 3 years ago
parent 6d29e519c9
commit 3a1abda2cb

@ -48,6 +48,13 @@ pub enum Query {
PipelinedQuery(Vec<ActionGroup>), PipelinedQuery(Vec<ActionGroup>),
} }
#[derive(Debug)]
enum NextCharError {
True,
False,
Missing,
}
type ParseResult<T> = Result<T, ParseError>; type ParseResult<T> = Result<T, ParseError>;
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
@ -108,12 +115,17 @@ impl<'a> Parser<'a> {
fn incr_cursor(&mut self) { fn incr_cursor(&mut self) {
self.cursor += 1; self.cursor += 1;
} }
fn will_cursor_give_char(&self, ch: u8, true_if_nothing_ahead: bool) -> bool { fn will_cursor_give_char(&self, ch: u8, this_if_nothing_ahead: bool) -> ParseResult<bool> {
self.buffer self.buffer.get(self.cursor).map_or(
.get(self.cursor) if this_if_nothing_ahead {
.map_or(true_if_nothing_ahead, |v| *v == ch) Ok(true)
} else {
Err(ParseError::NotEnough)
},
|v| Ok(*v == ch),
)
} }
fn will_cursor_give_linefeed(&self) -> bool { fn will_cursor_give_linefeed(&self) -> ParseResult<bool> {
self.will_cursor_give_char(b'\n', false) self.will_cursor_give_char(b'\n', false)
} }
fn parse_into_usize(bytes: &[u8]) -> ParseResult<usize> { fn parse_into_usize(bytes: &[u8]) -> ParseResult<usize> {
@ -152,7 +164,7 @@ impl<'a> Parser<'a> {
// also push the cursor ahead because we want to ignore the LF char // also push the cursor ahead because we want to ignore the LF char
// as read_until won't skip the newline // as read_until won't skip the newline
let ret = Self::parse_into_usize(&our_chunk[1..])?; let ret = Self::parse_into_usize(&our_chunk[1..])?;
if self.will_cursor_give_linefeed() { if self.will_cursor_give_linefeed()? {
// check if the cursor points at the terminal character LF // check if the cursor points at the terminal character LF
// as it does, we can freely move past // as it does, we can freely move past
self.incr_cursor(); self.incr_cursor();
@ -176,7 +188,7 @@ impl<'a> Parser<'a> {
// excluding the '&' char (so 1..) // excluding the '&' char (so 1..)
// also push the cursor ahead // also push the cursor ahead
let ret = Self::parse_into_usize(&our_chunk[1..])?; let ret = Self::parse_into_usize(&our_chunk[1..])?;
if self.will_cursor_give_linefeed() { if self.will_cursor_give_linefeed()? {
// check if the cursor points at the terminal character LF // check if the cursor points at the terminal character LF
// since it does, we'll move past // since it does, we'll move past
self.incr_cursor(); self.incr_cursor();
@ -196,7 +208,7 @@ impl<'a> Parser<'a> {
// Now we want to read the element itself // Now we want to read the element itself
let mut ret = Vec::with_capacity(element_size); let mut ret = Vec::with_capacity(element_size);
ret.extend_from_slice(self.read_until(element_size)?); ret.extend_from_slice(self.read_until(element_size)?);
if self.will_cursor_give_linefeed() { if self.will_cursor_give_linefeed()? {
// check if the cursor points at the terminal character LF // check if the cursor points at the terminal character LF
// We'll just return this since that's all we have to do! // We'll just return this since that's all we have to do!
// we just checked that the cursor points at the LF, so let's also move ahead before returning // we just checked that the cursor points at the LF, so let's also move ahead before returning
@ -227,7 +239,7 @@ impl<'a> Parser<'a> {
// The below line defaults to false if no item is there in the buffer // The below line defaults to false if no item is there in the buffer
// or it checks if the next time is a \r char; if it is, then it is the beginning // or it checks if the next time is a \r char; if it is, then it is the beginning
// of the next query // of the next query
if self.will_cursor_give_char(b'\r', true) { if self.will_cursor_give_char(b'\r', true)? {
Ok((Query::SimpleQuery(single_group), self.cursor)) Ok((Query::SimpleQuery(single_group), self.cursor))
} else { } else {
// the next item isn't the beginning of a query but something else? // the next item isn't the beginning of a query but something else?
@ -241,7 +253,7 @@ impl<'a> Parser<'a> {
for _ in 0..number_of_queries { for _ in 0..number_of_queries {
queries.push(self.parse_next_actiongroup()?); queries.push(self.parse_next_actiongroup()?);
} }
if self.will_cursor_give_char(b'\r', true) { if self.will_cursor_give_char(b'\r', true)? {
Ok((Query::PipelinedQuery(queries), self.cursor)) Ok((Query::PipelinedQuery(queries), self.cursor))
} else { } else {
Err(ParseError::UnexpectedByte) Err(ParseError::UnexpectedByte)
@ -277,11 +289,20 @@ fn test_metaframe_parse() {
#[test] #[test]
fn test_cursor_next_char() { fn test_cursor_next_char() {
let bytes = &[b'\n']; let bytes = &[b'\n'];
assert!(Parser::new(&bytes[..]).will_cursor_give_char(b'\n', false)); assert!(Parser::new(&bytes[..])
.will_cursor_give_char(b'\n', false)
.unwrap());
let bytes = &[]; let bytes = &[];
assert!(Parser::new(&bytes[..]).will_cursor_give_char(b'\r', true)); assert!(Parser::new(&bytes[..])
.will_cursor_give_char(b'\r', true)
.unwrap());
let bytes = &[]; let bytes = &[];
assert!(!Parser::new(&bytes[..]).will_cursor_give_char(b'\n', false)); assert!(
Parser::new(&bytes[..])
.will_cursor_give_char(b'\n', false)
.unwrap_err()
== ParseError::NotEnough
);
} }
#[test] #[test]
@ -399,6 +420,10 @@ fn test_query_fail_not_enough() {
Parser::new(&metaframe).parse().unwrap_err(), Parser::new(&metaframe).parse().unwrap_err(),
ParseError::NotEnough ParseError::NotEnough
); );
}
#[test]
fn test_query_incomplete_dataframes() {
let metaframe_and_dataframe_layout = "\r2\n*1\n#2\n&2\n".as_bytes(); let metaframe_and_dataframe_layout = "\r2\n*1\n#2\n&2\n".as_bytes();
// we got the number of queries and the size of the data group, but no data; this is incomplete // we got the number of queries and the size of the data group, but no data; this is incomplete
assert_eq!( assert_eq!(
@ -407,4 +432,29 @@ fn test_query_fail_not_enough() {
.unwrap_err(), .unwrap_err(),
ParseError::NotEnough ParseError::NotEnough
); );
let metaframe_and_dataframe_layout = "\r2\n*1\n#2\n&2\n#3\nGET\n".as_bytes();
// we got the number of queries and the size of the data group, but a part of the data; this is incomplete
assert_eq!(
Parser::new(&metaframe_and_dataframe_layout)
.parse()
.unwrap_err(),
ParseError::NotEnough
);
// incomplete sayan? that shouldn't work anyway heh ;)
let metaframe_and_dataframe_layout = "\r2\n*1\n#2\n&2\n#3\nGET\n#5\nsaya".as_bytes();
// we got the number of queries and the size of the data group, but a part of the data; this is incomplete
assert_eq!(
Parser::new(&metaframe_and_dataframe_layout)
.parse()
.unwrap_err(),
ParseError::NotEnough
);
let metaframe_and_dataframe_layout = "\r2\n*1\n#2\n&2\n#3\nGET\n#5\nsayan".as_bytes();
// we got the entire thing but not a newline; technically this is incomplete
assert_eq!(
Parser::new(&metaframe_and_dataframe_layout)
.parse()
.unwrap_err(),
ParseError::NotEnough
);
} }

Loading…
Cancel
Save