json-syntax 0.5.1

Strict JSON parsing and mapping library
Documentation
use super::{array, object, Context, Error, Parse, Parser, ValueOrParse};
use crate::{object::Key, Array, NumberBuf, Object, String, Value};
use decoded_char::DecodedChar;
use locspan::{Loc, Location, Meta};
use locspan_derive::*;

/// Value fragment.
#[derive(
	Clone,
	PartialEq,
	Eq,
	PartialOrd,
	Ord,
	Hash,
	Debug,
	StrippedPartialEq,
	StrippedEq,
	StrippedPartialOrd,
	StrippedOrd,
	StrippedHash,
)]
#[stripped_ignore(F)]
pub enum Fragment<F> {
	Value(Value<Location<F>>),
	BeginArray,
	BeginObject(#[stripped_deref] Loc<Key, F>),
}

impl<F> From<Value<Location<F>>> for Fragment<F> {
	fn from(v: Value<Location<F>>) -> Self {
		Self::Value(v)
	}
}

impl<F: Clone> Parse<F> for Fragment<F> {
	fn parse_in<E, C>(
		parser: &mut Parser<F, E, C>,
		context: Context,
	) -> Result<Loc<Self, F>, Loc<Error<E, F>, F>>
	where
		C: Iterator<Item = Result<DecodedChar, E>>,
	{
		parser.skip_whitespaces()?;

		let value = match parser.peek_char()? {
			Some('n') => <()>::parse_in(parser, context)?.map(|()| Value::Null),
			Some('t' | 'f') => bool::parse_in(parser, context)?.map(Value::Boolean),
			Some('0'..='9' | '-') => NumberBuf::parse_in(parser, context)?.map(Value::Number),
			Some('"') => String::parse_in(parser, context)?.map(Value::String),
			Some('[') => match array::StartFragment::parse_in(parser, context)? {
				Meta(array::StartFragment::Empty, loc) => Loc(Value::Array(Array::new()), loc),
				Meta(array::StartFragment::NonEmpty, loc) => return Ok(Loc(Self::BeginArray, loc)),
			},
			Some('{') => match object::StartFragment::parse_in(parser, context)? {
				Meta(object::StartFragment::Empty, loc) => Loc(Value::Object(Object::new()), loc),
				Meta(object::StartFragment::NonEmpty(key), loc) => {
					return Ok(Loc(Self::BeginObject(key), loc))
				}
			},
			unexpected => return Err(Loc(Error::unexpected(unexpected), parser.position.last())),
		};

		parser.skip_trailing_whitespaces(context)?;

		Ok(value.map(Self::Value))
	}
}

impl<F: Clone> Parse<F> for Value<Location<F>> {
	fn parse_in<E, C>(
		parser: &mut Parser<F, E, C>,
		context: Context,
	) -> Result<Loc<Self, F>, Loc<Error<E, F>, F>>
	where
		C: Iterator<Item = Result<DecodedChar, E>>,
	{
		enum Item<F> {
			Array(Loc<Array<Location<F>>, F>),
			ArrayItem(Loc<Array<Location<F>>, F>),
			Object(Loc<Object<Location<F>>, F>),
			ObjectEntry(Loc<Object<Location<F>>, F>, Loc<Key, F>),
		}

		let mut stack: Vec<Item<F>> = vec![];
		let mut value: Option<Loc<Value<Location<F>>, F>> = None;

		fn stack_context<F>(stack: &[Item<F>], root: Context) -> Context {
			match stack.last() {
				Some(Item::Array(_) | Item::ArrayItem(_)) => Context::Array,
				Some(Item::Object(_)) => Context::ObjectKey,
				Some(Item::ObjectEntry(_, _)) => Context::ObjectValue,
				None => root,
			}
		}

		loop {
			match stack.pop() {
				None => match Fragment::value_or_parse(
					value.take(),
					parser,
					stack_context(&stack, context),
				)? {
					Meta(Fragment::Value(value), loc) => break Ok(Loc(value, loc)),
					Meta(Fragment::BeginArray, loc) => {
						stack.push(Item::ArrayItem(Loc(Array::new(), loc)))
					}
					Meta(Fragment::BeginObject(key), loc) => {
						stack.push(Item::ObjectEntry(Loc(Object::new(), loc), key))
					}
				},
				Some(Item::Array(Meta(array, loc))) => {
					match array::ContinueFragment::parse_in(parser, stack_context(&stack, context))?
					{
						Meta(array::ContinueFragment::Item, comma_loc) => {
							stack.push(Item::ArrayItem(Loc(array, loc.with(comma_loc.span()))))
						}
						Meta(array::ContinueFragment::End, closing_loc) => {
							parser.skip_trailing_whitespaces(stack_context(&stack, context))?;
							value = Some(Loc(Value::Array(array), loc.with(closing_loc.span())))
						}
					}
				}
				Some(Item::ArrayItem(Meta(mut array, loc))) => {
					match Fragment::value_or_parse(value.take(), parser, Context::Array)? {
						Meta(Fragment::Value(value), value_loc) => {
							let value_span = value_loc.span();
							array.push(Loc(value, value_loc));
							stack.push(Item::Array(Loc(array, loc.with(value_span))));
						}
						Meta(Fragment::BeginArray, value_loc) => {
							stack.push(Item::Array(Loc(array, loc.with(value_loc.span()))));
							stack.push(Item::ArrayItem(Loc(Array::new(), value_loc)))
						}
						Meta(Fragment::BeginObject(value_key), value_loc) => {
							stack.push(Item::Array(Loc(array, loc.with(value_loc.span()))));
							stack.push(Item::ObjectEntry(Loc(Object::new(), value_loc), value_key))
						}
					}
				}
				Some(Item::Object(Meta(object, loc))) => match object::ContinueFragment::parse_in(
					parser,
					stack_context(&stack, context),
				)? {
					Meta(object::ContinueFragment::Entry(key), comma_key_loc) => stack.push(
						Item::ObjectEntry(Loc(object, loc.with(comma_key_loc.span())), key),
					),
					Meta(object::ContinueFragment::End, closing_loc) => {
						parser.skip_trailing_whitespaces(stack_context(&stack, context))?;
						value = Some(Loc(Value::Object(object), loc.with(closing_loc.span())))
					}
				},
				Some(Item::ObjectEntry(Meta(mut object, loc), key)) => {
					match Fragment::value_or_parse(value.take(), parser, Context::ObjectValue)? {
						Meta(Fragment::Value(value), value_loc) => {
							let value_span = value_loc.span();
							object.push(key, Loc(value, value_loc));
							stack.push(Item::Object(Loc(object, loc.with(value_span))));
						}
						Meta(Fragment::BeginArray, value_loc) => {
							stack.push(Item::ObjectEntry(
								Loc(object, loc.with(value_loc.span())),
								key,
							));
							stack.push(Item::ArrayItem(Loc(Array::new(), value_loc)))
						}
						Meta(Fragment::BeginObject(value_key), value_loc) => {
							stack.push(Item::ObjectEntry(
								Loc(object, loc.with(value_loc.span())),
								key,
							));
							stack.push(Item::ObjectEntry(Loc(Object::new(), value_loc), value_key))
						}
					}
				}
			}
		}
	}
}