Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Deserialize] Split out peek logic from seed generic #1102

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 45 additions & 54 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ impl ParserNumber {
}
}

enum PeekResult {
Char(u8),
EndOfSeq,
EofSeq,
Eof,
}

impl<'de, R: Read<'de>> Deserializer<R> {
/// The `Deserializer::end` method should be called after a value has been fully deserialized.
/// This allows the `Deserializer` to validate that the input stream is at the end or that it
Expand Down Expand Up @@ -259,6 +266,28 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
}

fn peek_next_elem<const END: u8>(&mut self, first: &mut bool) -> Result<PeekResult> {
match tri!(self.parse_whitespace()) {
Some(c) if c == END => Ok(PeekResult::EndOfSeq),
Some(b',') if !*first => {
self.eat_char();
self.parse_whitespace().map(|c| match c {
Some(c) => PeekResult::Char(c),
None => PeekResult::Eof,
})
}
Some(b) => {
if *first {
*first = false;
Ok(PeekResult::Char(b))
} else {
Err(self.peek_error(ErrorCode::ExpectedNextOrEnd { end: END }))
}
}
None => Ok(PeekResult::EofSeq),
}
}

#[cold]
fn peek_invalid_type(&mut self, exp: &dyn Expected) -> Error {
let err = match self.peek_or_null().unwrap_or(b'\x00') {
Expand Down Expand Up @@ -1163,8 +1192,8 @@ impl<'de, R: Read<'de>> Deserializer<R> {
Some(_) => {
if accept_comma {
return Err(self.peek_error(match frame {
b'[' => ErrorCode::ExpectedListCommaOrEnd,
b'{' => ErrorCode::ExpectedObjectCommaOrEnd,
b'[' => ErrorCode::ExpectedNextOrEnd { end: b'[' },
b'{' => ErrorCode::ExpectedNextOrEnd { end: b'{' },
_ => unreachable!(),
}));
} else {
Expand Down Expand Up @@ -1909,7 +1938,7 @@ struct SeqAccess<'a, R: 'a> {
first: bool,
}

impl<'a, R: 'a> SeqAccess<'a, R> {
impl<'de, 'a, R: Read<'de> + 'a> SeqAccess<'a, R> {
fn new(de: &'a mut Deserializer<R>) -> Self {
SeqAccess { de, first: true }
}
Expand All @@ -1922,31 +1951,12 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> {
where
T: de::DeserializeSeed<'de>,
{
let peek = match tri!(self.de.parse_whitespace()) {
Some(b']') => {
return Ok(None);
}
Some(b',') if !self.first => {
self.de.eat_char();
tri!(self.de.parse_whitespace())
}
Some(b) => {
if self.first {
self.first = false;
Some(b)
} else {
return Err(self.de.peek_error(ErrorCode::ExpectedListCommaOrEnd));
}
}
None => {
return Err(self.de.peek_error(ErrorCode::EofWhileParsingList));
}
};

match peek {
Some(b']') => Err(self.de.peek_error(ErrorCode::TrailingComma)),
Some(_) => Ok(Some(tri!(seed.deserialize(&mut *self.de)))),
None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
match tri!(self.de.peek_next_elem::<b']'>(&mut self.first)) {
PeekResult::EndOfSeq => Ok(None),
PeekResult::Char(b']') => Err(self.de.peek_error(ErrorCode::TrailingComma)),
PeekResult::Char(_) => Ok(Some(tri!(seed.deserialize(&mut *self.de)))),
PeekResult::Eof => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
PeekResult::EofSeq => Err(self.de.peek_error(ErrorCode::EofWhileParsingList)),
}
}
}
Expand All @@ -1969,32 +1979,13 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> {
where
K: de::DeserializeSeed<'de>,
{
let peek = match tri!(self.de.parse_whitespace()) {
Some(b'}') => {
return Ok(None);
}
Some(b',') if !self.first => {
self.de.eat_char();
tri!(self.de.parse_whitespace())
}
Some(b) => {
if self.first {
self.first = false;
Some(b)
} else {
return Err(self.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd));
}
}
None => {
return Err(self.de.peek_error(ErrorCode::EofWhileParsingObject));
}
};

match peek {
Some(b'"') => seed.deserialize(MapKey { de: &mut *self.de }).map(Some),
Some(b'}') => Err(self.de.peek_error(ErrorCode::TrailingComma)),
Some(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)),
None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
match tri!(self.de.peek_next_elem::<b'}'>(&mut self.first)) {
PeekResult::EndOfSeq => Ok(None),
PeekResult::Char(b'"') => seed.deserialize(MapKey { de: &mut *self.de }).map(Some),
PeekResult::Char(b'}') => Err(self.de.peek_error(ErrorCode::TrailingComma)),
PeekResult::Char(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)),
PeekResult::Eof => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)),
PeekResult::EofSeq => Err(self.de.peek_error(ErrorCode::EofWhileParsingObject)),
}
}

Expand Down
15 changes: 6 additions & 9 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ impl Error {
| ErrorCode::EofWhileParsingString
| ErrorCode::EofWhileParsingValue => Category::Eof,
ErrorCode::ExpectedColon
| ErrorCode::ExpectedListCommaOrEnd
| ErrorCode::ExpectedObjectCommaOrEnd
| ErrorCode::ExpectedNextOrEnd { .. }
| ErrorCode::ExpectedSomeIdent
| ErrorCode::ExpectedSomeValue
| ErrorCode::ExpectedDoubleQuote
Expand Down Expand Up @@ -255,11 +254,8 @@ pub(crate) enum ErrorCode {
/// Expected this character to be a `':'`.
ExpectedColon,

/// Expected this character to be either a `','` or a `']'`.
ExpectedListCommaOrEnd,

/// Expected this character to be either a `','` or a `'}'`.
ExpectedObjectCommaOrEnd,
/// Expected this character to be either ',' or end.
ExpectedNextOrEnd { end: u8 },

/// Expected to parse either a `true`, `false`, or a `null`.
ExpectedSomeIdent,
Expand Down Expand Up @@ -356,8 +352,9 @@ impl Display for ErrorCode {
ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
ErrorCode::ExpectedColon => f.write_str("expected `:`"),
ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
ErrorCode::ExpectedNextOrEnd { end } => {
write!(f, "expected `,` or `{}`", *end as char)
}
ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
ErrorCode::ExpectedDoubleQuote => f.write_str("expected `\"`"),
Expand Down