use crate::ParseError;
use crate::{ujson, JsonNumber, String};
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Event<'a, 'b> {
StartObject,
EndObject,
StartArray,
EndArray,
Key(String<'a, 'b>),
String(String<'a, 'b>),
Number(JsonNumber<'a, 'b>),
Bool(bool),
Null,
EndDocument,
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UnexpectedState {
StateMismatch,
InvalidEscapeToken,
InvalidUnicodeEscape,
BufferCapacityExceeded,
InvalidSliceBounds,
}
#[derive(Debug, PartialEq)]
pub enum State {
None,
Key(usize),
String(usize),
Number(usize),
}
pub struct ParserState {
pub evts: [Option<ujson::Event>; 2],
}
impl ParserState {
pub fn new() -> Self {
Self {
evts: core::array::from_fn(|_| None),
}
}
}
impl Default for ParserState {
fn default() -> Self {
Self::new()
}
}
pub trait PullParser {
fn next(&mut self) -> Option<Result<Event<'_, '_>, ParseError>> {
match self.next_event() {
Ok(Event::EndDocument) => None,
other => Some(other),
}
}
fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError>;
}
pub(crate) struct ContentRange;
impl ContentRange {
pub fn number_start_from_current(current_pos: usize) -> usize {
current_pos.saturating_sub(1) }
pub fn string_content_bounds_from_content_start(
content_start: usize,
current_pos: usize,
) -> (usize, usize) {
let content_end = current_pos.saturating_sub(1); if content_start > content_end {
(content_start, content_start)
} else {
(content_start, content_end)
}
}
pub fn unicode_escape_bounds(current_pos: usize) -> (usize, usize, usize) {
let hex_start = current_pos.saturating_sub(4); let hex_end = current_pos; let escape_start = current_pos.saturating_sub(6); (hex_start, hex_end, escape_start)
}
pub fn end_position_excluding_delimiter(current_pos: usize) -> usize {
current_pos.saturating_sub(1)
}
pub fn number_end_position(current_pos: usize, use_full_span: bool) -> usize {
if use_full_span {
current_pos
} else {
current_pos.saturating_sub(1)
}
}
}
pub trait DataSource<'input, 'scratch> {
fn get_borrowed_slice(
&'input self,
start: usize,
end: usize,
) -> Result<&'input [u8], ParseError>;
fn get_unescaped_slice(&'scratch self) -> Result<&'scratch [u8], ParseError>;
fn has_unescaped_content(&self) -> bool;
}
#[derive(Debug, PartialEq)]
pub enum ContentPiece<'input, 'scratch> {
Input(&'input [u8]),
Scratch(&'scratch [u8]),
}
impl<'input, 'scratch> ContentPiece<'input, 'scratch>
where
'input: 'scratch,
{
pub fn into_string(self) -> Result<String<'input, 'scratch>, ParseError> {
match self {
ContentPiece::Input(bytes) => {
let content_str = from_utf8(bytes)?;
Ok(String::Borrowed(content_str))
}
ContentPiece::Scratch(bytes) => {
let content_str = from_utf8(bytes)?;
Ok(String::Unescaped(content_str))
}
}
}
pub fn as_bytes(&self) -> &'scratch [u8] {
match self {
ContentPiece::Input(bytes) => bytes,
ContentPiece::Scratch(bytes) => bytes,
}
}
}
pub fn from_utf8(v: &[u8]) -> Result<&str, ParseError> {
core::str::from_utf8(v).map_err(Into::into)
}
pub fn get_content_piece<'input, 'scratch, D>(
source: &'input D,
start_pos: usize,
current_pos: usize,
) -> Result<ContentPiece<'input, 'scratch>, ParseError>
where
'input: 'scratch,
D: ?Sized + DataSource<'input, 'scratch>,
{
if source.has_unescaped_content() {
source.get_unescaped_slice().map(ContentPiece::Scratch)
} else {
let (content_start, content_end) =
ContentRange::string_content_bounds_from_content_start(start_pos, current_pos);
source
.get_borrowed_slice(content_start, content_end)
.map(ContentPiece::Input)
}
}