use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::deserialize::numbers::{float, int, uint};
use crate::deserialize::string::string;
use core::fmt::Debug;
use nom::branch::alt;
use nom::bytes::streaming::tag_no_case;
use nom::character::streaming::char;
use nom::combinator::{map, not, value};
use nom::error::ParseError;
use nom::sequence::delimited;
use nom::{AsChar, Compare, IResult, Input, Needed, Parser};
mod string;
mod numbers;
mod adapter;
mod error;
pub use adapter::from_iter;
pub use adapter::from_str;
pub use adapter::Deserializer;
pub use error::DeserializeError;
#[derive(Clone, Debug, PartialEq)]
pub struct AnnotatedList(Box<Item>, Vec<Item>);
#[derive(Clone, PartialEq, Debug)]
pub enum Item {
AnnotatedList(AnnotatedList),
List(Vec<Item>),
String(String),
Bool(bool),
Int(i64),
UInt(u64),
Float(f64),
Null
}
pub fn literal<'a, I: Input + ToString + Compare<&'a str>, E: ParseError<I>>(input: I) -> IResult<I, Item, E>
where <I as Input>::Item: AsChar,
String: FromIterator<I>
{
alt((
list::<I, E>,
bool::<I, E>,
float::<I, E>,
uint::<I, E>,
int::<I, E>,
null::<I, E>,
string::<I, E>
)).parse(input)
}
#[test]
pub fn test_literal() {
use nom::Finish;
use alloc::vec;
match literal::<&str, DeserializeError<&str>>("(a(1))").finish() {
Ok(item) => assert_eq!(
item,
("", Item::List(vec![Item::AnnotatedList(AnnotatedList(
Box::new(Item::String("a".to_string())),
vec![
Item::UInt(1)
],
))]))
),
Err(err) => panic!("{}", err),
}
match literal::<&str, DeserializeError<&str>>("(a(1),b(-2),c(3.14),null(true))").finish() {
Ok(item) => assert_eq!(
item,
("", Item::List(vec![
Item::AnnotatedList(AnnotatedList(
Box::new(Item::String("a".to_string())),
vec![
Item::UInt(1)
],
)), Item::AnnotatedList(AnnotatedList(
Box::new(Item::String("b".to_string())),
vec![
Item::Int(-2)
],
)),
Item::AnnotatedList(AnnotatedList(
Box::new(Item::String("c".to_string())),
vec![
Item::Float(3.14)
],
)),
Item::AnnotatedList(AnnotatedList(
Box::new(Item::Null),
vec![
Item::Bool(true)
],
)),
]))
),
Err(err) => panic!("{}", err),
}
}
fn strip_whitespace_or_comma<'a, I: Input, E: ParseError<I>, P: Parser<I, Error = E, Output = Item>>(mut parser: P, quit_on: char) -> impl FnMut(I) -> IResult<I, Vec<Item>, E>
where <I as Input>::Item: AsChar {
move |input| {
let mut outputs: Vec<Item> = Vec::new();
let mut input: I = input;
let predicate = |(_, item): &(_, <I as Input>::Item)| {
match item.as_char() {
' ' => false,
'\r' => false,
'\n' => false,
'\t' => false,
',' => false,
_ => true
}
};
loop {
match input.iter_indices().find(predicate) {
None => break Err(nom::Err::Incomplete(Needed::Unknown)),
Some(found) => {
if found.1.as_char() == quit_on {
break Ok((input.take_from(found.0), outputs));
}
match parser.parse(input.take_from(found.0)) {
Ok(out) => {
input = out.0;
if found.0 == 0 {
match out.1 {
Item::List(value) => {
match outputs.pop() {
None => outputs.push(Item::List(value)),
Some(key) => {
outputs.push(Item::AnnotatedList(AnnotatedList(Box::new(key), value)));
}
}
}
_ => outputs.push(out.1)
}
} else {
outputs.push(out.1)
}
}
Err(error) => match error {
nom::Err::Incomplete(_) => break Err(error),
nom::Err::Error(_) => break Ok((input, outputs)),
nom::Err::Failure(_) => break Err(error)
}
}
}
}
}
}
}
pub fn list<'a, I: Input + ToString + Compare<&'a str>, E: ParseError<I>>(input: I) -> IResult<I, Item, E>
where <I as Input>::Item: AsChar,
String: FromIterator<I>
{
let parse = strip_whitespace_or_comma(alt((
literal::<I, E>,
map(not::<I, E, _>(char::<I, E>(')')), |_| Item::Null),
)), ')');
map(delimited(
char('('),
parse,
char(')')
), |a| Item::List(a)).parse(input)
}
#[test]
pub fn test_list() {
use nom::Finish;
use alloc::vec;
assert_eq!(
list::<&str, DeserializeError<&str>>("(1,-2)nya").finish().unwrap(),
("nya", Item::List(vec![
Item::UInt(1),
Item::Int(-2)
]))
);
assert_eq!(
list::<&str, DeserializeError<&str>>("(a,b)nya").finish().unwrap(),
("nya", Item::List(vec![
Item::String("a".to_string()),
Item::String("b".to_string()),
]))
);
assert_eq!(
list::<&str, DeserializeError<&str>>("(1\n -2 )nya").finish().unwrap(),
("nya", Item::List(vec![
Item::UInt(1),
Item::Int(-2)
]))
);
assert_eq!(
list::<&str, DeserializeError<&str>>("(a\n b )nya").finish().unwrap(),
("nya", Item::List(vec![
Item::String("a".to_string()),
Item::String("b".to_string()),
]))
);
assert_eq!(
list::<&str, DeserializeError<&str>>("(1\n -2 nya)nya").finish().unwrap(),
("nya", Item::List(vec![
Item::UInt(1),
Item::Int(-2),
Item::String("nya".to_string())
]))
);
assert_eq!(
list::<&str, DeserializeError<&str>>("(a\n b nya)nya").finish().unwrap(),
("nya", Item::List(vec![
Item::String("a".to_string()),
Item::String("b".to_string()),
Item::String("nya".to_string())
]))
);
}
pub fn bool<'a, I: Input + Compare<&'a str>, E: ParseError<I> >(input: I) -> IResult<I, Item, E> {
map(alt((
value(true, tag_no_case("true")),
value(false, tag_no_case("false"))
)), Item::Bool).parse(input)
}
#[test]
pub fn test_bool() {
use nom::Finish;
assert_eq!(
bool::<&str, DeserializeError<&str>>("truenya").finish().unwrap(),
("nya", Item::Bool(true))
);
assert_eq!(
bool::<&str, DeserializeError<&str>>("falsenya").finish().unwrap(),
("nya", Item::Bool(false))
);
}
pub fn null<'a, I: Input + Compare<&'a str>, E: ParseError<I>>(input: I) -> IResult<I, Item, E> {
value(Item::Null, tag_no_case("null")).parse(input)
}
#[test]
pub fn test_null() {
use nom::Finish;
assert_eq!(
null::<&str, DeserializeError<&str>>("nullnya").finish().unwrap(),
("nya", Item::Null)
);
}