use alloc::{
format,
string::{String, ToString},
vec::Vec,
}; use core::{
char::from_u32 as char_from_u32,
str::{self, from_utf8, FromStr, Utf8Error},
};
use unicode_ident::{is_xid_continue, is_xid_start};
if !matches!(
&res,
Err(Error::UnderscoreAtBeginning | Error::InvalidIntegerDigit { .. })
) { // Advance past the number suffix self.skip_identifier();
}
let integer_ron = &src_backup[..src_backup.len() - suffix_bytes.len()];
let c = if c == '\\' { matchself.parse_escape(EscapeEncoding::Utf8, true)? { // we know that this byte is an ASCII character
EscapeCharacter::Ascii(b) => char::from(b),
EscapeCharacter::Utf8(c) => c,
}
} else {
c
};
/// Only returns true if the char after `ident` cannot belong /// to an identifier. pubfn check_ident(&mutself, ident: &str) -> bool { self.check_str(ident) && !self.check_ident_other_char(ident.len())
}
/// Check which type of struct we are currently parsing. The parsing state /// is only changed in case of an error, to provide a better position. /// /// [`NewtypeMode::NoParensMeanUnit`] detects (tuple) structs by a leading /// opening bracket and reports a unit struct otherwise. /// [`NewtypeMode::InsideNewtype`] skips an initial check for unit structs, /// and means that any leading opening bracket is not considered to open /// a (tuple) struct but to be part of the structs inner contents. /// /// [`TupleMode::ImpreciseTupleOrNewtype`] only performs a cheap, O(1), /// single-identifier lookahead check to distinguish tuple structs from /// non-tuple structs. /// [`TupleMode::DifferentiateNewtype`] performs an expensive, O(N), look- /// ahead over the entire next value tree, which can span the entirety of /// the remaining document in the worst case. pubfn check_struct_type(
&mutself,
newtype: NewtypeMode,
tuple: TupleMode,
) -> Result<StructType> { fn check_struct_type_inner(
parser: &mut Parser,
newtype: NewtypeMode,
tuple: TupleMode,
) -> Result<StructType> { if matches!(newtype, NewtypeMode::NoParensMeanUnit) && !parser.consume_char('(') { return Ok(StructType::Unit);
}
parser.skip_ws()?;
// Check for `Ident()`, which could be // - a zero-field struct or tuple (variant) // - an unwrapped newtype around a unit if matches!(newtype, NewtypeMode::NoParensMeanUnit) && parser.check_char(')') { return Ok(StructType::EmptyTuple);
}
if parser.skip_identifier().is_some() {
parser.skip_ws()?;
match parser.peek_char() { // Definitely a struct with named fields
Some(':') => return Ok(StructType::Named), // Definitely a tuple-like struct with fields
Some(',') => {
parser.skip_next_char();
parser.skip_ws()?; if parser.check_char(')') { // A one-element tuple could be a newtype return Ok(StructType::NewtypeTuple);
} // Definitely a tuple struct with more than one field return Ok(StructType::NonNewtypeTuple);
} // Either a newtype or a tuple struct
Some(')') => return Ok(StructType::NewtypeTuple), // Something else, let's investigate further
Some(_) | None => (),
};
}
if matches!(tuple, TupleMode::ImpreciseTupleOrNewtype) { return Ok(StructType::AnyTuple);
}
// Skip ahead to see if the value is followed by another value while braces > 0 { // Skip spurious braces in comments, strings, and characters
parser.skip_ws()?; let cursor_backup = parser.cursor; if parser.char().is_err() {
parser.set_cursor(cursor_backup);
} let cursor_backup = parser.cursor; match parser.string() {
Ok(_) => (), // prevent quadratic complexity backtracking for unterminated string
Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err),
Err(_) => parser.set_cursor(cursor_backup),
} let cursor_backup = parser.cursor; // we have already checked for strings, which subsume base64 byte strings match parser.byte_string_no_base64() {
Ok(_) => (), // prevent quadratic complexity backtracking for unterminated byte string
Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err),
Err(_) => parser.set_cursor(cursor_backup),
}
if more_than_one {
Ok(StructType::NonNewtypeTuple)
} else {
Ok(StructType::NewtypeTuple)
}
}
// Create a temporary working copy let backup_cursor = self.cursor;
let result = check_struct_type_inner(self, newtype, tuple);
if result.is_ok() { // Revert the parser to before the struct type check self.set_cursor(backup_cursor);
}
result
}
/// Only returns true if the char after `ident` cannot belong /// to an identifier. pubfn consume_ident(&mutself, ident: &str) -> bool { ifself.check_ident(ident) { self.advance_bytes(ident.len());
loop { let ident = self.identifier()?; let extension = Extensions::from_ident(ident)
.ok_or_else(|| Error::NoSuchExtension(ident.into()))?;
extensions |= extension;
let comma = self.comma()?;
// If we have no comma but another item, return an error if !comma && self.check_ident_other_char(0) { return Err(Error::ExpectedComma);
}
// If there's no comma, assume the list ended. // If there is, it might be a trailing one, thus we only // continue the loop if we get an ident char. if !comma || !self.check_ident_other_char(0) { break;
}
}
pubfn skip_identifier(&mutself) -> Option<&'a str> { #[allow(clippy::nonminimal_bool)] ifself.check_str("b\"") // byte string
|| self.check_str("b'") // byte literal
|| self.check_str("br#") // raw byte string
|| self.check_str("br\"") // raw byte string
|| self.check_str("r\"") // raw string
|| self.check_str("r#\"") // raw string
|| self.check_str("r##") // raw string
|| false
{ return None;
}
ifself.check_str("r#") { // maybe a raw identifier let len = self.next_chars_while_from_len(2, is_ident_raw_char); if len > 0 { let ident = &self.src()[2..2 + len]; self.advance_bytes(2 + len); return Some(ident);
} return None;
}
iflet Some(c) = self.peek_char() { // maybe a normal identifier if is_ident_first_char(c) { let len =
c.len_utf8() + self.next_chars_while_from_len(c.len_utf8(), is_xid_continue); let ident = &self.src()[..len]; self.advance_bytes(len); return Some(ident);
}
}
None
}
pubfn identifier(&mutself) -> Result<&'a str> { let first = self.peek_char_or_eof()?; if !is_ident_first_char(first) { if is_ident_raw_char(first) { let ident_bytes = self.next_chars_while_len(is_ident_raw_char); return Err(Error::SuggestRawIdentifier( self.src()[..ident_bytes].into(),
));
}
return Err(Error::ExpectedIdentifier);
}
// If the next 2-3 bytes signify the start of a (raw) (byte) string // literal, return an error. #[allow(clippy::nonminimal_bool)] ifself.check_str("b\"") // byte string
|| self.check_str("b'") // byte literal
|| self.check_str("br#") // raw byte string
|| self.check_str("br\"") // raw byte string
|| self.check_str("r\"") // raw string
|| self.check_str("r#\"") // raw string
|| self.check_str("r##") // raw string
|| false
{ return Err(Error::ExpectedIdentifier);
}
let length = ifself.check_str("r#") { let cursor_backup = self.cursor;
self.advance_bytes(2);
// Note: it's important to check this before advancing forward, so that // the value-type deserializer can fall back to parsing it differently. if !matches!(self.peek_char(), Some(c) if is_ident_raw_char(c)) { self.set_cursor(cursor_backup); return Err(Error::ExpectedIdentifier);
}
self.next_chars_while_len(is_ident_raw_char)
} elseif first == 'r' { let std_ident_length = self.next_chars_while_len(is_xid_continue); let raw_ident_length = self.next_chars_while_len(is_ident_raw_char);
if raw_ident_length > std_ident_length { return Err(Error::SuggestRawIdentifier( self.src()[..raw_ident_length].into(),
));
}
std_ident_length
} else { let std_ident_length = first.len_utf8()
+ self.next_chars_while_from_len(first.len_utf8(), is_xid_continue); let raw_ident_length = self.next_chars_while_len(is_ident_raw_char);
if raw_ident_length > std_ident_length { return Err(Error::SuggestRawIdentifier( self.src()[..raw_ident_length].into(),
));
}
std_ident_length
};
let ident = &self.src()[..length]; self.advance_bytes(length);
Ok(ident)
}
pubfn next_bytes_is_float(&mutself) -> bool { iflet Some(c) = self.peek_char() { let skip = match c { '+' | '-' => 1,
_ => 0,
}; let valid_float_len = self.next_chars_while_from_len(skip, is_float_char); let valid_int_len = self.next_chars_while_from_len(skip, is_int_char);
valid_float_len > valid_int_len
} else { false
}
}
pubfn skip_ws(&mutself) -> Result<()> { if (self.cursor.last_ws_len != WS_CURSOR_UNCLOSED_LINE)
&& ((self.cursor.pre_ws_cursor + self.cursor.last_ws_len) < self.cursor.cursor)
{ // the last whitespace is disjoint from this one, we need to track a new one self.cursor.pre_ws_cursor = self.cursor.cursor;
}
// FIXME @juntyr: remove in v0.13, since only byte_string_no_base64 will // be used ifself.consume_char('"') { let base64_str = self.escaped_string()?; let base64_result = ParsedByteStr::try_from_base64(&base64_str);
match base64_result {
Some(byte_str) => Err(expected_byte_string_found_base64(&base64_str, &byte_str)),
None => Err(Error::ExpectedByteString),
}
} elseifself.consume_char('r') { let base64_str = self.raw_string()?; let base64_result = ParsedByteStr::try_from_base64(&base64_str);
fn escaped_byte_buf(&mutself, encoding: EscapeEncoding) -> Result<(ParsedByteStr<'a>, usize)> { // Checking for '"' and '\\' separately is faster than searching for both at the same time let str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?; let escape = self.src()[..str_end].find('\\');
iflet Some(escape) = escape { // Now check if escaping is used inside the string letmut i = escape; letmut s = self.src().as_bytes()[..i].to_vec();
loop { self.advance_bytes(i + 1);
matchself.parse_escape(encoding, false)? {
EscapeCharacter::Ascii(c) => s.push(c),
EscapeCharacter::Utf8(c) => match c.len_utf8() { 1 => s.push(c as u8),
len => { let start = s.len();
s.extend(core::iter::repeat(0).take(len));
c.encode_utf8(&mut s[start..]);
}
},
}
// Checking for '"' and '\\' separately is faster than searching for both at the same time let new_str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?; let new_escape = self.src()[..new_str_end].find('\\');
iflet Some(new_escape) = new_escape {
s.extend_from_slice(&self.src().as_bytes()[..new_escape]);
i = new_escape;
} else {
s.extend_from_slice(&self.src().as_bytes()[..new_str_end]); // Advance to the end of the string + 1 for the `"`. break Ok((ParsedByteStr::Allocated(s), new_str_end + 1));
}
}
} else { let s = &self.src().as_bytes()[..str_end];
// Advance by the number of bytes of the string + 1 for the `"`.
Ok((ParsedByteStr::Slice(s), str_end + 1))
}
}
fn raw_byte_buf(&mutself) -> Result<(ParsedByteStr<'a>, usize)> { let num_hashes = self.next_chars_while_len(|c| c == '#'); let hashes = &self.src()[..num_hashes]; self.advance_bytes(num_hashes);
self.expect_char('"', Error::ExpectedString)?;
let ending = ["\"", hashes].concat(); let i = self.src().find(&ending).ok_or(Error::ExpectedStringEnd)?;
let s = &self.src().as_bytes()[..i];
// Advance by the number of bytes of the byte string // + `num_hashes` + 1 for the `"`.
Ok((ParsedByteStr::Slice(s), i + num_hashes + 1))
}
fn decode_ascii_escape(&mutself) -> Result<u8> { letmut n = 0; for _ in0..2 {
n <<= 4; let byte = self.next_char()?; let decoded = Self::decode_hex(byte)?;
n |= decoded;
}
// c is an ASCII character that can be losslessly cast to u8 match c as u8 {
c @ b'0'..=b'9' => Ok(c - b'0'),
c @ b'a'..=b'f' => Ok(10 + c - b'a'),
c @ b'A'..=b'F' => Ok(10 + c - b'A'),
_ => Err(Error::InvalidEscape("Non-hex digit found")),
}
}
fn parse_escape(&mutself, encoding: EscapeEncoding, is_char: bool) -> Result<EscapeCharacter> { let c = matchself.next_char()? { '\'' => EscapeCharacter::Ascii(b'\''), '"' => EscapeCharacter::Ascii(b'"'), '\\' => EscapeCharacter::Ascii(b'\\'), 'n' => EscapeCharacter::Ascii(b'\n'), 'r' => EscapeCharacter::Ascii(b'\r'), 't' => EscapeCharacter::Ascii(b'\t'), '0' => EscapeCharacter::Ascii(b'\0'), 'x' => { // Fast exit for ascii escape in byte string let b: u8 = self.decode_ascii_escape()?; iflet EscapeEncoding::Binary = encoding { return Ok(EscapeCharacter::Ascii(b));
}
// Fast exit for ascii character in UTF-8 string letmut bytes = [b, 0, 0, 0]; iflet Ok(Some(c)) = from_utf8(&bytes[..=0]).map(|s| s.chars().next()) { return Ok(EscapeCharacter::Utf8(c));
}
if is_char { // Character literals are not allowed to use multiple byte // escapes to build a unicode character return Err(Error::InvalidEscape( "Not a valid byte-escaped Unicode character",
));
}
// UTF-8 character needs up to four bytes and we have already // consumed one, so at most three to go for i in1..4 { if !self.consume_str(r"\x") { return Err(Error::InvalidEscape( "Not a valid byte-escaped Unicode character",
));
}
bytes[i] = self.decode_ascii_escape()?;
// Check if we now have a valid UTF-8 character iflet Ok(Some(c)) = from_utf8(&bytes[..=i]).map(|s| s.chars().next()) { return Ok(EscapeCharacter::Utf8(c));
}
}
return Err(Error::InvalidEscape( "Not a valid byte-escaped Unicode character",
));
} 'u' => { self.expect_char('{', Error::InvalidEscape("Missing { in Unicode escape"))?;
letmut bytes: u32 = 0; letmut num_digits = 0;
while num_digits < 6 { let byte = self.peek_char_or_eof()?;
if num_digits == 0 { return Err(Error::InvalidEscape( "Expected 1-6 digits, got 0 digits in Unicode escape",
));
}
self.expect_char( '}',
Error::InvalidEscape("No } at the end of Unicode escape"),
)?; let c = char_from_u32(bytes).ok_or(Error::InvalidEscape( "Not a valid Unicode-escaped character",
))?;
impl Float for ParsedFloat { fn parse(float: &str) -> Result<Self> { let value = f64::from_str(float).map_err(|_| Error::ExpectedFloat)?;
#[allow(clippy::cast_possible_truncation)] if value.total_cmp(&f64::from(value as f32)).is_eq() {
Ok(ParsedFloat::F32(value as f32))
} else {
Ok(ParsedFloat::F64(value))
}
}
let err = crate::from_str::<bytes::Bytes>("r\"SGVsbG8gcm9uIQ==\"").unwrap_err();
assert_eq!(format!("{}", err.code), "Expected the Rusty byte string b\"Hello ron!\" but found the ambiguous base64 string \"SGVsbG8gcm9uIQ==\" instead");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.