extern crate alloc;
use crate::data::*;
use crate::dataarray::*;
use crate::databytes::*;
use crate::dataobject::*;
use core::fmt; use alloc::string::{String, ToString};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ParseError {
UnexpectedEof,
UnexpectedCharacter(char),
ExpectedCharacter(char),
ExpectedValue,
ExpectedComma,
ExpectedColon,
InvalidEscapeSequence(String),
InvalidUnicodeEscape(String),
InvalidNumber(String),
TrailingCharacters(String),
Message(String), }
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::UnexpectedEof => write!(f, "Unexpected end of input"),
ParseError::UnexpectedCharacter(c) => write!(f, "Unexpected character: '{}'", c),
ParseError::ExpectedCharacter(c) => write!(f, "Expected character: '{}'", c),
ParseError::ExpectedValue => write!(f, "Expected JSON value"),
ParseError::ExpectedComma => write!(f, "Expected comma separator"),
ParseError::ExpectedColon => write!(f, "Expected colon separator"),
ParseError::InvalidEscapeSequence(s) => write!(f, "Invalid escape sequence: {}", s),
ParseError::InvalidUnicodeEscape(s) => write!(f, "Invalid Unicode escape sequence: {}", s),
ParseError::InvalidNumber(s) => write!(f, "Invalid number format: {}", s),
ParseError::TrailingCharacters(s) => write!(f, "Trailing characters after JSON value: {}", s),
ParseError::Message(msg) => write!(f, "JSON parsing error: {}", msg),
}
}
}
#[cfg(not(feature="no_std_support"))]
impl std::error::Error for ParseError {}
pub fn object_to_string(o: DataObject) -> String {
let mut s = String::new(); write_object(&mut s, &o).expect("Writing to String should not fail");
s
}
pub fn array_to_string(a: DataArray) -> String {
let mut s = String::new(); write_array(&mut s, &a).expect("Writing to String should not fail");
s
}
fn write_object<W: fmt::Write>(writer: &mut W, o: &DataObject) -> fmt::Result {
writer.write_char('{')?;
let mut first = true;
for key in o.clone().keys() {
if !first {
writer.write_char(',')?;
}
first = false;
writer.write_char('"')?;
write_escaped_str(writer, &key)?;
writer.write_char('"')?;
writer.write_char(':')?;
let p = o.get_property(&key);
write_data(writer, &p)?;
}
writer.write_char('}')
}
fn write_array<W: fmt::Write>(writer: &mut W, a: &DataArray) -> fmt::Result {
writer.write_char('[')?;
let mut first = true;
for p in a.clone().objects() {
if !first {
writer.write_char(',')?;
}
first = false;
write_data(writer, &p)?;
}
writer.write_char(']')
}
fn write_data<W: fmt::Write>(writer: &mut W, data: &Data) -> fmt::Result {
match data {
Data::DNull => writer.write_str("null"),
Data::DBoolean(b) => writer.write_str(if *b { "true" } else { "false" }),
Data::DInt(i) => write!(writer, "{}", i),
Data::DFloat(f) => write!(writer, "{}", f), Data::DString(s) => {
writer.write_char('"')?;
write_escaped_str(writer, s)?;
writer.write_char('"')
}
Data::DBytes(bytes_ref) => {
let bytes_data = DataBytes::get(*bytes_ref); let s = bytes_data.to_hex_string(); writer.write_char('"')?;
writer.write_str(&s)?;
writer.write_char('"')
}
Data::DObject(obj_ref) => {
let obj = DataObject::get(*obj_ref);
write_object(writer, &obj)
}
Data::DArray(arr_ref) => {
let arr = DataArray::get(*arr_ref);
write_array(writer, &arr)
}
}
}
fn write_escaped_str<W: fmt::Write>(writer: &mut W, s: &str) -> fmt::Result {
for c in s.chars() {
match c {
'"' => writer.write_str("\\\"")?,
'\\' => writer.write_str("\\\\")?,
'/' => writer.write_str("\\/")?, '\x08' => writer.write_str("\\b")?, '\x0c' => writer.write_str("\\f")?, '\n' => writer.write_str("\\n")?, '\r' => writer.write_str("\\r")?, '\t' => writer.write_str("\\t")?, '\x00'..='\x1f' => write!(writer, "\\u{:04x}", c as u32)?,
_ => writer.write_char(c)?,
}
}
Ok(())
}
pub fn object_from_string(s: &str) -> Result<DataObject, ParseError> {
let mut input = s.trim();
if input.is_empty() {
return Err(ParseError::UnexpectedEof);
}
let (obj, remaining) = parse_object(&mut input)?;
if !remaining.trim().is_empty() {
obj.decr(); Err(ParseError::TrailingCharacters(remaining.trim().to_string()))
} else {
Ok(obj)
}
}
pub fn array_from_string(s: &str) -> Result<DataArray, ParseError> {
let mut input = s.trim();
if input.is_empty() {
return Err(ParseError::UnexpectedEof);
}
let (arr, remaining) = parse_array(&mut input)?;
if !remaining.trim().is_empty() {
arr.decr(); Err(ParseError::TrailingCharacters(remaining.trim().to_string()))
} else {
Ok(arr)
}
}
fn parse_hex4<I>(chars: &mut I) -> Result<u32, ParseError>
where
I: Iterator<Item = char>,
{
let mut hex_str = String::with_capacity(4);
for _ in 0..4 {
match chars.next() {
Some(hc) if hc.is_ascii_hexdigit() => {
hex_str.push(hc);
}
Some(bad_char) => {
return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{}<-- invalid char '{}'", hex_str, bad_char
)));
}
None => {
return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{} (unexpected EOF)", hex_str
)));
}
}
}
u32::from_str_radix(&hex_str, 16)
.map_err(|_| ParseError::InvalidUnicodeEscape(format!("\\u{} (internal parsing failed)", hex_str)))
}
pub fn unescape(s: &str) -> Result<String, ParseError> {
let mut output = String::with_capacity(s.len()); let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == '\\' {
match chars.next() {
Some('"') => output.push('"'),
Some('\\') => output.push('\\'),
Some('/') => output.push('/'),
Some('b') => output.push('\x08'), Some('f') => output.push('\x0c'), Some('n') => output.push('\n'), Some('r') => output.push('\r'), Some('t') => output.push('\t'), Some('u') => {
let code1 = parse_hex4(&mut chars)?;
if (0xD800..=0xDBFF).contains(&code1) {
if chars.peek() == Some(&'\\') {
chars.next(); if chars.peek() == Some(&'u') {
chars.next(); let code2 = parse_hex4(&mut chars)?;
if (0xDC00..=0xDFFF).contains(&code2) {
let combined = (((code1 - 0xD800) * 0x400) + (code2 - 0xDC00)) + 0x10000;
match core::char::from_u32(combined) {
Some(unicode_char) => output.push(unicode_char),
None => return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{:04X}\\u{:04X} (combined to invalid code point {})", code1, code2, combined
))),
}
} else {
return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{:04X} followed by non-low surrogate \\u{:04X}", code1, code2
)));
}
} else {
return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{:04X} followed by invalid escape sequence", code1
)));
}
} else {
return Err(ParseError::InvalidUnicodeEscape(format!(
"Lone high surrogate \\u{:04X}", code1
)));
}
} else {
match core::char::from_u32(code1) {
Some(unicode_char) => output.push(unicode_char),
None => return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{:04X} (invalid code point)", code1
))),
}
}
}
Some(other) => return Err(ParseError::InvalidEscapeSequence(format!("\\{}", other))),
None => return Err(ParseError::UnexpectedEof), }
} else {
if ('\x00'..='\x1f').contains(&c) {
return Err(ParseError::UnexpectedCharacter(c));
}
output.push(c);
}
}
Ok(output)
}
fn skip_whitespace(input: &mut &str) {
*input = input.trim_start();
}
fn consume_char(input: &mut &str, expected: char) -> Result<(), ParseError> {
if input.starts_with(expected) {
*input = &input[expected.len_utf8()..];
Ok(())
} else {
let found = input.chars().next();
match found {
Some(c) => Err(ParseError::UnexpectedCharacter(c)), None => Err(ParseError::UnexpectedEof),
}
}
}
fn parse_string_content(input: &mut &str) -> Result<String, ParseError> {
let mut output = String::new(); let mut consumed_bytes = 0;
fn parse_hex4_slice(slice: &str) -> Result<(u32, usize), ParseError> {
if slice.len() < 4 {
return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{}... (unexpected EOF)", slice
)));
}
let hex_str = &slice[..4];
match u32::from_str_radix(hex_str, 16) {
Ok(code) => Ok((code, 4)),
Err(_) => Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{} (parsing failed)", hex_str
))),
}
}
loop {
let current_slice = &input[consumed_bytes..];
let next_special = current_slice.find(|c: char| c == '\\' || c == '"');
match next_special {
Some(index) => {
let special_char = current_slice[index..].chars().next().unwrap();
output.push_str(¤t_slice[..index]);
consumed_bytes += index;
if special_char == '"' {
consumed_bytes += '"'.len_utf8(); break; } else {
consumed_bytes += '\\'.len_utf8();
let escape_slice = &input[consumed_bytes..];
let mut escape_chars = escape_slice.chars();
match escape_chars.next() {
Some('"') => { output.push('"'); consumed_bytes += '"'.len_utf8(); }
Some('\\') => { output.push('\\'); consumed_bytes += '\\'.len_utf8(); }
Some('/') => { output.push('/'); consumed_bytes += '/'.len_utf8(); }
Some('b') => { output.push('\x08'); consumed_bytes += 'b'.len_utf8(); }
Some('f') => { output.push('\x0c'); consumed_bytes += 'f'.len_utf8(); }
Some('n') => { output.push('\n'); consumed_bytes += 'n'.len_utf8(); }
Some('r') => { output.push('\r'); consumed_bytes += 'r'.len_utf8(); }
Some('t') => { output.push('\t'); consumed_bytes += 't'.len_utf8(); }
Some('u') => { consumed_bytes += 'u'.len_utf8(); let (code1, hex_len1) = parse_hex4_slice(&input[consumed_bytes..])?;
consumed_bytes += hex_len1;
if (0xD800..=0xDBFF).contains(&code1) {
if input.get(consumed_bytes..consumed_bytes + 2) == Some("\\u") {
consumed_bytes += 2; let (code2, hex_len2) = parse_hex4_slice(&input[consumed_bytes..])?;
consumed_bytes += hex_len2;
if (0xDC00..=0xDFFF).contains(&code2) {
let combined = (((code1 - 0xD800) * 0x400) + (code2 - 0xDC00)) + 0x10000;
match core::char::from_u32(combined) {
Some(unicode_char) => output.push(unicode_char),
None => return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{:04X}\\u{:04X} (combined to invalid code point {})", code1, code2, combined
))),
}
} else {
return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{:04X} followed by non-low surrogate \\u{:04X}", code1, code2
)));
}
} else {
return Err(ParseError::InvalidUnicodeEscape(format!(
"Lone high surrogate \\u{:04X}", code1
)));
}
} else {
match core::char::from_u32(code1) {
Some(unicode_char) => output.push(unicode_char),
None => return Err(ParseError::InvalidUnicodeEscape(format!(
"\\u{:04X} (invalid code point)", code1
))),
}
}
}
Some(other) => return Err(ParseError::InvalidEscapeSequence(format!("\\{}", other))),
None => return Err(ParseError::UnexpectedEof), }
}
}
None => {
return Err(ParseError::UnexpectedEof); }
}
}
*input = &input[consumed_bytes..];
Ok(output)
}
fn parse_number(input: &mut &str) -> Result<Data, ParseError> {
skip_whitespace(input);
let mut len = 0;
let mut has_dot = false;
let mut has_exp = false;
for c in input.chars() {
match c {
'0'..='9' => len += c.len_utf8(),
'.' if !has_dot => { has_dot = true;
len += c.len_utf8();
}
'e' | 'E' if !has_exp => { has_exp = true;
has_dot = true; len += c.len_utf8();
if let Some(sign) = input.get(len..).and_then(|s| s.chars().next()) {
if sign == '+' || sign == '-' {
len += sign.len_utf8();
}
}
}
_ => break, }
}
if len == 0 {
return Err(ParseError::ExpectedValue); }
let num_str = &input[..len];
*input = &input[len..];
if !has_dot && !has_exp {
if let Ok(i) = num_str.parse::<i64>() {
return Ok(Data::DInt(i));
}
}
if let Ok(f) = num_str.parse::<f64>() {
Ok(Data::DFloat(f))
} else {
Err(ParseError::InvalidNumber(num_str.to_string()))
}
}
fn parse_value<'a>(input: &mut &'a str) -> Result<(Data, &'a str), ParseError> {
skip_whitespace(input);
if input.is_empty() {
return Err(ParseError::UnexpectedEof);
}
let first_char = match input.chars().next() {
Some(c) => c,
None => return Err(ParseError::UnexpectedEof), };
match first_char {
'"' => {
consume_char(input, '"')?; let content = parse_string_content(input)?; Ok((Data::DString(content), *input))
}
'{' => {
let (obj, remaining) = parse_object(input)?;
obj.incr();
Ok((Data::DObject(obj.data_ref), remaining))
}
'[' => {
let (arr, remaining) = parse_array(input)?;
arr.incr();
Ok((Data::DArray(arr.data_ref), remaining))
}
't' => {
if input.starts_with("true") {
*input = &input["true".len()..];
Ok((Data::DBoolean(true), *input))
} else {
Err(ParseError::UnexpectedCharacter('t')) }
}
'f' => {
if input.starts_with("false") {
*input = &input["false".len()..];
Ok((Data::DBoolean(false), *input))
} else {
Err(ParseError::UnexpectedCharacter('f'))
}
}
'n' => {
if input.starts_with("null") {
*input = &input["null".len()..];
Ok((Data::DNull, *input))
} else {
Err(ParseError::UnexpectedCharacter('n'))
}
}
'-' | '0'..='9' => {
let num_data = parse_number(input)?;
Ok((num_data, *input))
}
_ => Err(ParseError::UnexpectedCharacter(first_char)), }
}
#[allow(unused_assignments)]
fn parse_object<'a>(input: &mut &'a str) -> Result<(DataObject, &'a str), ParseError> {
consume_char(input, '{')?;
skip_whitespace(input);
let mut obj = DataObject::new();
let mut first = true;
if input.starts_with('}') {
consume_char(input, '}')?;
return Ok((obj, *input));
}
loop {
if !first {
skip_whitespace(input);
if input.starts_with('}') {
obj.decr(); return Err(ParseError::ExpectedComma); }
consume_char(input, ',')?;
skip_whitespace(input);
}
if input.starts_with('}') {
if first { obj.decr();
return Err(ParseError::ExpectedCharacter('"')); } else { obj.decr();
return Err(ParseError::ExpectedCharacter('"')); }
}
skip_whitespace(input);
consume_char(input, '"')?; let key = parse_string_content(input)?;
skip_whitespace(input);
consume_char(input, ':')?;
skip_whitespace(input);
let (val, _) = parse_value(input)?;
obj.set_property(&key, val.clone());
if val.is_object() { val.object().decr(); }
if val.is_array() { val.array().decr(); }
skip_whitespace(input);
if input.starts_with('}') {
consume_char(input, '}')?;
first = false; break; } else if input.starts_with(',') {
first = false; }
else {
obj.decr(); let found = input.chars().next();
match found {
Some(c) => return Err(ParseError::UnexpectedCharacter(c)), None => return Err(ParseError::UnexpectedEof),
}
}
}
Ok((obj, *input))
}
#[allow(unused_assignments)]
fn parse_array<'a>(input: &mut &'a str) -> Result<(DataArray, &'a str), ParseError> {
consume_char(input, '[')?;
skip_whitespace(input);
let mut arr = DataArray::new();
let mut first = true;
if input.starts_with(']') {
consume_char(input, ']')?;
return Ok((arr, *input));
}
loop {
if !first {
skip_whitespace(input);
if input.starts_with(']') {
arr.decr(); return Err(ParseError::ExpectedComma); }
consume_char(input, ',')?;
skip_whitespace(input);
}
if input.starts_with(']') {
if first { arr.decr();
return Err(ParseError::ExpectedValue); } else { arr.decr();
return Err(ParseError::ExpectedValue); }
}
skip_whitespace(input); let (val, _) = parse_value(input)?;
arr.push_property(val.clone());
if val.is_object() { val.object().decr(); }
if val.is_array() { val.array().decr(); }
skip_whitespace(input);
if input.starts_with(']') {
consume_char(input, ']')?;
first = false; break; } else if input.starts_with(',') {
first = false; }
else {
arr.decr(); let found = input.chars().next();
match found {
Some(c) => return Err(ParseError::UnexpectedCharacter(c)), None => return Err(ParseError::UnexpectedEof),
}
}
}
Ok((arr, *input))
}
pub fn unescape_original(s:&str) -> String {
let s = str::replace(&s, "\\\"", "\"");
let s = str::replace(&s, "\\n", "\n");
let s = str::replace(&s, "\\r", "\r");
let s = str::replace(&s, "\\t", "\t");
let s = str::replace(&s, "\\\\", "\\");
s
}
pub fn escape_original(s:&str) -> String {
let s = str::replace(&s, "\\", "\\\\");
let s = str::replace(&s, "\"", "\\\"");
let s = str::replace(&s, "\n", "\\n");
let s = str::replace(&s, "\r", "\r");
let s = str::replace(&s, "\t", "\\t");
s
}
#[cfg(feature="no_std_support")]
mod str {
use alloc::string::String;
use alloc::vec::Vec; pub fn replace(s: &str, from: &str, to: &str) -> String {
s.split(from).collect::<Vec<&str>>().join(to)
}
}
#[cfg(not(feature="no_std_support"))]
use std::str;