use anyhow::{Result, anyhow};
use serde::{
de::{self, Deserialize, Deserializer},
ser::{Serialize, Serializer},
};
use serde_json::Value;
use serde_json::{Map, Number};
#[inline]
pub fn parse(text: &str) -> Result<Value> {
let input = text.trim();
if input.is_empty() {
return Ok(Value::Object(Map::new()));
}
let input = remove_comments(input);
let input = input.trim();
if input.is_empty() {
return Ok(Value::Object(Map::new()));
}
if input.as_bytes()[0] == b'{' {
let (value, _) = parse_nested_object(input, 0)?;
return Ok(value);
}
parse_jhon_object(input)
}
#[inline]
pub fn serialize(value: &Value) -> String {
let estimated_size = estimate_serialized_size(value);
let mut result = String::with_capacity(estimated_size);
serialize_compact(value, &mut result);
result
}
#[inline]
fn estimate_serialized_size(value: &Value) -> usize {
match value {
Value::Object(map) => {
let mut size = map.len() * 8; for (k, v) in map {
size += k.len() + estimate_serialized_size(v);
}
size
}
Value::Array(arr) => {
let mut size = arr.len() * 2; for v in arr {
size += estimate_serialized_size(v);
}
size
}
Value::String(s) => s.len() + 2, Value::Number(_) => 16, Value::Bool(_) => 5,
Value::Null => 4,
}
}
pub fn serialize_pretty(value: &Value, indent: &str) -> String {
let mut result = String::new();
serialize_pretty_with_depth(value, indent, 0, false, &mut result);
result
}
pub struct Jhon;
impl Jhon {
pub fn to_string<T: Serialize>(value: &T) -> Result<String> {
let value = serde_json::to_value(value)?;
Ok(serialize(&value))
}
pub fn to_string_pretty<T: Serialize>(value: &T, indent: &str) -> Result<String> {
let value = serde_json::to_value(value)?;
Ok(serialize_pretty(&value, indent))
}
pub fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T> {
let value = parse(s)?;
T::deserialize(value).map_err(|e| anyhow!("Deserialization error: {}", e))
}
pub fn from_str_with_deserializer<'de, T: Deserialize<'de>, D: Deserializer<'de>>(
deserializer: D,
) -> Result<T> {
T::deserialize(deserializer).map_err(|e| anyhow!("Deserialization error: {}", e))
}
}
pub mod jhon {
use super::*;
pub fn serialize<T: Serialize, S: Serializer>(
value: &T,
serializer: S,
) -> Result<S::Ok, S::Error> {
let jhon_string = Jhon::to_string(value).map_err(serde::ser::Error::custom)?;
jhon_string.serialize(serializer)
}
pub fn deserialize<'de, T: Deserialize<'de>, D: Deserializer<'de>>(
deserializer: D,
) -> Result<T, D::Error> {
let json_value = serde_json::Value::deserialize(deserializer)?;
T::deserialize(json_value).map_err(de::Error::custom)
}
}
pub fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T> {
Jhon::from_str(s)
}
pub fn to_string<T: Serialize>(value: &T) -> Result<String> {
Jhon::to_string(value)
}
pub fn to_string_pretty<T: Serialize>(value: &T, indent: &str) -> Result<String> {
Jhon::to_string_pretty(value, indent)
}
const BB: u8 = b'b'; const TT: u8 = b't'; const NN: u8 = b'n'; const FF: u8 = b'f'; const RR: u8 = b'r'; const QU: u8 = b'"'; const BS: u8 = b'\\'; const UU: u8 = b'u'; const __: u8 = 0;
static ESCAPE: [u8; 256] = [
UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, ];
static CLASSIFICATION: [u8; 256] = {
const C: u8 = 0; const W: u8 = 0x01; const D: u8 = 0x02; const I: u8 = 0x04; const S: u8 = 0x08; let mut table = [C; 256];
table[b'\t' as usize] = W;
table[b'\n' as usize] = W;
table[b'\r' as usize] = W;
table[b' ' as usize] = W;
let mut i = b'0';
while i <= b'9' {
table[i as usize] = D | I;
i += 1;
}
let mut i = b'a';
while i <= b'z' {
table[i as usize] = I;
table[(i - 32) as usize] = I; i += 1;
}
table[b'_' as usize] = I;
table[b'-' as usize] = I;
table[b'=' as usize] = S;
table[b'{' as usize] = S;
table[b'}' as usize] = S;
table[b'[' as usize] = S;
table[b']' as usize] = S;
table[b'"' as usize] = S;
table[b'\'' as usize] = S;
table[b',' as usize] = S;
table[b'#' as usize] = S;
table
};
#[inline(always)]
fn is_whitespace(b: u8) -> bool {
CLASSIFICATION[b as usize] & 0x01 != 0
}
#[inline(always)]
fn is_digit(b: u8) -> bool {
CLASSIFICATION[b as usize] & 0x02 != 0
}
#[inline(always)]
fn is_identifier_char(b: u8) -> bool {
CLASSIFICATION[b as usize] & 0x04 != 0
}
#[derive(Clone, Copy)]
struct Parser<'a> {
input: &'a [u8],
pos: usize,
}
impl<'a> Parser<'a> {
fn new(input: &'a [u8]) -> Self {
Self { input, pos: 0 }
}
fn current(&self) -> Option<u8> {
self.input.get(self.pos).copied()
}
fn advance(&mut self) -> Option<u8> {
let c = self.current()?;
self.pos += 1;
Some(c)
}
fn skip_byte(&mut self, byte: u8) {
while self.current() == Some(byte) {
self.pos += 1;
}
}
fn skip_whitespace(&mut self) {
while self.pos + 8 <= self.input.len() {
let chunk = u64::from_le_bytes([
self.input[self.pos],
self.input[self.pos + 1],
self.input[self.pos + 2],
self.input[self.pos + 3],
self.input[self.pos + 4],
self.input[self.pos + 5],
self.input[self.pos + 6],
self.input[self.pos + 7],
]);
let has_non_ws = {
let bytes = chunk.to_le_bytes();
!bytes.iter().all(|&b| is_whitespace(b))
};
if has_non_ws {
break;
}
self.pos += 8;
}
while self.pos < self.input.len() && is_whitespace(self.input[self.pos]) {
self.pos += 1;
}
}
fn skip_spaces_and_tabs(&mut self) {
while self.pos < self.input.len() {
let b = self.input[self.pos];
if b == b' ' || b == b'\t' {
self.pos += 1;
} else {
break;
}
}
}
fn parse_string(&mut self, quote: u8) -> Result<String> {
self.advance();
let start = self.pos;
while self.pos + 8 <= self.input.len() {
let chunk = u64::from_le_bytes([
self.input[self.pos],
self.input[self.pos + 1],
self.input[self.pos + 2],
self.input[self.pos + 3],
self.input[self.pos + 4],
self.input[self.pos + 5],
self.input[self.pos + 6],
self.input[self.pos + 7],
]);
let diff_quote = chunk ^ u64::from_le_bytes([quote; 8]);
let diff_bs = chunk ^ u64::from_le_bytes([b'\\'; 8]);
if (diff_quote.wrapping_sub(0x0101010101010101) & !diff_quote & 0x8080808080808080) != 0
|| (diff_bs.wrapping_sub(0x0101010101010101) & !diff_bs & 0x8080808080808080) != 0
{
let end = self.pos + 8;
while self.pos < end {
let b = self.input[self.pos];
if b == quote {
let s = unsafe {
std::str::from_utf8_unchecked(&self.input[start..self.pos])
};
self.pos += 1;
return Ok(s.to_string());
}
if b == b'\\' {
break;
}
self.pos += 1;
}
if self.input[self.pos] == b'\\' {
break;
}
}
self.pos += 8;
}
let mut has_escape = false;
while self.pos < self.input.len() {
let b = self.input[self.pos];
if b == quote {
let s = std::str::from_utf8(&self.input[start..self.pos])
.map_err(|_| anyhow!("Invalid UTF-8 in string"))?;
self.pos += 1;
return Ok(s.to_string());
}
if b == b'\\' {
has_escape = true;
break;
}
self.pos += 1;
}
if !has_escape {
return Err(anyhow!("Unterminated string"));
}
let mut result = String::from_utf8_lossy(&self.input[start..self.pos]).into_owned();
while self.pos < self.input.len() {
let b = self.advance().ok_or_else(|| anyhow!("Unterminated string"))?;
if b == quote {
return Ok(result);
}
if b == b'\\' {
let escaped = self.advance().ok_or_else(|| anyhow!("Incomplete escape sequence"))?;
result.push(match escaped {
b'n' => '\n',
b'r' => '\r',
b't' => '\t',
b'b' => '\u{08}',
b'f' => '\u{0c}',
b'\\' => '\\',
b'"' | b'\'' => escaped as char,
b'u' => {
let mut code = 0u16;
for _ in 0..4 {
let h = self.advance().ok_or_else(|| anyhow!("Incomplete Unicode escape"))?;
let digit = h.wrapping_sub(b'0');
let digit = if digit <= 9 {
digit
} else {
let h = h.wrapping_sub(b'a');
if h <= 5 {
h + 10
} else {
let h = h.wrapping_sub(b'A');
if h <= 5 {
h + 10
} else {
return Err(anyhow!("Invalid Unicode escape"));
}
}
};
code = (code << 4) | digit as u16;
}
char::from_u32(code as u32).ok_or_else(|| anyhow!("Invalid Unicode code point"))?
}
_ => escaped as char,
});
} else {
result.push(b as char);
}
}
Err(anyhow!("Unterminated string"))
}
fn parse_raw_string(&mut self) -> Result<String> {
self.advance();
let mut hash_count = 0;
while self.current() == Some(b'#') {
hash_count += 1;
self.advance();
}
if self.current() != Some(b'"') {
return Err(anyhow!("Expected opening quote after r and # symbols in raw string"));
}
self.advance();
let start = self.pos;
while self.pos < self.input.len() {
if self.input[self.pos] == b'"' {
if self.pos + hash_count < self.input.len() {
let is_closing = (1..=hash_count).all(|j| {
self.input.get(self.pos + j) == Some(&b'#')
});
if is_closing {
let s = std::str::from_utf8(&self.input[start..self.pos])
.map_err(|_| anyhow!("Invalid UTF-8 in raw string"))?
.to_string();
self.pos += hash_count + 1;
return Ok(s);
}
}
}
self.pos += 1;
}
Err(anyhow!("Unterminated raw string"))
}
fn parse_number(&mut self) -> Result<Value> {
let start = self.pos;
let sign = if self.current() == Some(b'-') {
self.advance();
-1i64
} else {
1i64
};
let mut value: u64 = 0;
let mut has_digits = false;
while self.pos < self.input.len() {
let b = self.input[self.pos];
if is_digit(b) {
if value > (u64::MAX / 10) {
return self.parse_number_slow(start);
}
value = value.wrapping_mul(10).wrapping_add((b - b'0') as u64);
self.pos += 1;
has_digits = true;
} else if b == b'_' {
self.pos += 1;
} else {
break;
}
}
if !has_digits {
return Err(anyhow!("Invalid number"));
}
if self.current() == Some(b'.') {
return self.parse_number_slow(start);
}
let value = if sign < 0 {
value.wrapping_neg()
} else {
value
} as i64;
Ok(Value::Number(Number::from(value)))
}
fn parse_number_slow(&mut self, start: usize) -> Result<Value> {
let mut has_digits = false;
while self.pos < self.input.len() {
let b = self.input[self.pos];
if is_digit(b) {
has_digits = true;
self.pos += 1;
} else if b == b'_' || b == b'.' || b == b'e' || b == b'E' || b == b'+' || b == b'-' {
self.pos += 1;
} else {
break;
}
}
if !has_digits {
return Err(anyhow!("Invalid number"));
}
let num_str: String = self.input[start..self.pos]
.iter()
.filter(|&&b| b != b'_')
.map(|&b| b as char)
.collect();
if let Ok(i) = num_str.parse::<i64>() {
return Ok(Value::Number(Number::from(i)));
}
let f = num_str.parse::<f64>()
.map_err(|_| anyhow!("Could not parse number"))?;
Number::from_f64(f).map(Value::Number).ok_or_else(|| anyhow!("Invalid number value"))
}
fn parse_array(&mut self) -> Result<(Value, usize)> {
self.advance();
let start = self.pos;
let mut depth = 1usize;
let end = loop {
if self.pos >= self.input.len() {
return Err(anyhow!("Unterminated array"));
}
match self.input[self.pos] {
b'[' => depth += 1,
b']' => {
depth -= 1;
if depth == 0 {
break self.pos;
}
}
_ => {}
}
self.pos += 1;
};
let estimated_elements = self.input[start..end].iter().filter(|&&b| b == b',').count() + 1;
self.pos = start;
let mut elements = Vec::with_capacity(estimated_elements);
self.skip_spaces_and_tabs();
while self.current() != Some(b']') {
if let Some(value) = self.parse_value()? {
elements.push(value);
}
self.skip_byte(b'\n');
self.skip_byte(b',');
self.skip_spaces_and_tabs();
if self.pos >= self.input.len() {
return Err(anyhow!("Unterminated array"));
}
}
self.pos += 1; Ok((Value::Array(elements), self.pos))
}
fn parse_nested_object(&mut self) -> Result<(Value, usize)> {
self.advance();
let start = self.pos;
let mut depth = 1usize;
let end = loop {
if self.pos >= self.input.len() {
return Err(anyhow!("Unterminated nested object"));
}
match self.input[self.pos] {
b'{' => depth += 1,
b'}' => {
depth -= 1;
if depth == 0 {
break self.pos;
}
}
_ => {}
}
self.pos += 1;
};
let estimated_pairs = self.input[start..end].iter().filter(|&&b| b == b'=').count();
self.pos = start;
let mut map = Map::with_capacity(estimated_pairs.max(1));
self.skip_spaces_and_tabs();
while self.current() != Some(b'}') {
let key = self.parse_key()?;
self.skip_whitespace();
if self.current() != Some(b'=') {
return Err(anyhow!("Expected '=' after key in nested object"));
}
self.pos += 1;
self.skip_whitespace();
if let Some(value) = self.parse_value()? {
map.insert(key, value);
}
self.skip_byte(b'\n');
self.skip_byte(b',');
self.skip_spaces_and_tabs();
if self.pos >= self.input.len() {
return Err(anyhow!("Unterminated nested object"));
}
}
self.pos += 1; Ok((Value::Object(map), self.pos))
}
fn parse_key(&mut self) -> Result<String> {
self.skip_whitespace();
let quote = self.current();
if quote == Some(b'"') || quote == Some(b'\'') {
self.parse_string(quote.unwrap())
} else {
let start = self.pos;
while self.pos < self.input.len() && is_identifier_char(self.input[self.pos]) {
self.pos += 1;
}
if start == self.pos {
return Err(anyhow!("Empty key"));
}
unsafe {
Ok(std::str::from_utf8_unchecked(&self.input[start..self.pos]).to_string())
}
}
}
fn parse_value(&mut self) -> Result<Option<Value>> {
self.skip_whitespace();
let c = self.current().ok_or_else(|| anyhow!("Expected value"))?;
let result = match c {
b'"' | b'\'' => Some(Value::String(self.parse_string(c)?)),
b'r' | b'R' => Some(Value::String(self.parse_raw_string()?)),
b'[' => Some(self.parse_array()?.0),
b'{' => Some(self.parse_nested_object()?.0),
b'0'..=b'9' | b'-' => Some(self.parse_number()?),
b't' | b'f' => Some(self.parse_boolean()?),
b'n' => Some(self.parse_null()?),
_ => return Err(anyhow!("Unexpected character in value: {}", c as char)),
};
Ok(result)
}
fn parse_boolean(&mut self) -> Result<Value> {
if self.input.len() >= self.pos + 4
&& &self.input[self.pos..self.pos + 4] == b"true" {
self.pos += 4;
return Ok(Value::Bool(true));
} else if self.input.len() >= self.pos + 5
&& &self.input[self.pos..self.pos + 5] == b"false" {
self.pos += 5;
return Ok(Value::Bool(false));
}
Err(anyhow!("Invalid boolean value"))
}
fn parse_null(&mut self) -> Result<Value> {
if self.input.len() >= self.pos + 4
&& &self.input[self.pos..self.pos + 4] == b"null" {
self.pos += 4;
return Ok(Value::Null);
}
Err(anyhow!("Invalid null value"))
}
}
#[inline]
fn remove_comments(input: &str) -> String {
let bytes = input.as_bytes();
let len = bytes.len();
let has_slash = bytes.iter().any(|&b| b == b'/');
if !has_slash {
return input.to_string();
}
let mut result = Vec::with_capacity(len);
let mut i = 0;
while i < len {
while i + 8 < len {
let chunk = u64::from_le_bytes([
bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3],
bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7],
]);
let diff = chunk ^ u64::from_le_bytes([b'/'; 8]);
if (diff.wrapping_sub(0x0101010101010101) & !diff & 0x8080808080808080) != 0 {
break;
}
result.extend_from_slice(&bytes[i..i + 8]);
i += 8;
}
if i >= len {
break;
}
if bytes[i] == b'/' && i + 1 < len {
if bytes[i + 1] == b'/' {
i += 2;
while i < len && bytes[i] != b'\n' {
i += 1;
}
continue;
} else if bytes[i + 1] == b'*' {
i += 2;
while i < len {
if bytes[i] == b'*' && i + 1 < len && bytes[i + 1] == b'/' {
i += 2;
break;
}
i += 1;
}
continue;
}
}
result.push(bytes[i]);
i += 1;
}
unsafe { String::from_utf8_unchecked(result) }
}
#[inline]
fn parse_jhon_object(input: &str) -> Result<Value> {
let mut parser = Parser::new(input.as_bytes());
let estimated_pairs = input.as_bytes().iter().filter(|&&b| b == b'=').count();
let mut map = Map::with_capacity(estimated_pairs.max(1));
parser.skip_spaces_and_tabs();
while parser.pos < parser.input.len() {
let key = parser.parse_key()?;
parser.skip_whitespace();
if parser.current() != Some(b'=') {
return Err(anyhow!("Expected '=' after key"));
}
parser.pos += 1;
parser.skip_whitespace();
if let Some(value) = parser.parse_value()? {
map.insert(key, value);
}
parser.skip_byte(b'\n');
parser.skip_byte(b',');
parser.skip_spaces_and_tabs();
}
Ok(Value::Object(map))
}
fn parse_nested_object(input: &str, start_pos: usize) -> Result<(Value, usize)> {
let mut parser = Parser::new(input.as_bytes());
parser.pos = start_pos;
let (value, end) = parser.parse_nested_object()?;
Ok((value, end))
}
#[inline(always)]
fn serialize_compact(value: &Value, result: &mut String) {
match value {
Value::Object(map) if map.is_empty() => {}
Value::Object(map) => serialize_object_compact(map, result),
Value::Array(arr) if arr.is_empty() => result.push_str("[]"),
Value::Array(arr) => serialize_array_compact(arr, result),
Value::String(s) => serialize_string(s, result),
Value::Number(n) => serialize_number(n, result),
Value::Bool(b) => result.push_str(if *b { "true" } else { "false" }),
Value::Null => result.push_str("null"),
}
}
#[inline(always)]
fn serialize_object_compact(map: &Map<String, Value>, result: &mut String) {
let mut keys: Vec<&String> = map.keys().collect();
keys.sort();
let mut first = true;
for key in keys {
if !first {
result.push(',');
}
first = false;
serialize_key(key, result);
result.push('=');
let value = &map[key];
match value {
Value::Object(inner) if inner.is_empty() => result.push_str("{}"),
Value::Object(inner) => {
result.push('{');
serialize_object_compact(inner, result);
result.push('}');
}
_ => serialize_compact(value, result),
}
}
}
#[inline(always)]
fn serialize_array_compact(arr: &[Value], result: &mut String) {
result.push('[');
let mut first = true;
for value in arr {
if !first {
result.push(',');
}
first = false;
match value {
Value::Object(map) if map.is_empty() => result.push_str("{}"),
Value::Object(map) => {
result.push('{');
serialize_object_compact(map, result);
result.push('}');
}
_ => serialize_compact(value, result),
}
}
result.push(']');
}
#[inline(always)]
fn serialize_key(key: &str, result: &mut String) {
if needs_quoting(key) {
serialize_string(key, result);
} else {
result.push_str(key);
}
}
#[inline(always)]
fn serialize_string(s: &str, result: &mut String) {
result.push('"');
let bytes = s.as_bytes();
let mut i = 0;
while i + 8 <= bytes.len() {
let mut needs_escape = false;
for j in 0..8 {
if ESCAPE[bytes[i + j] as usize] != 0 {
needs_escape = true;
if j > 0 {
let safe_part = unsafe { std::str::from_utf8_unchecked(&bytes[i..i + j]) };
result.push_str(safe_part);
}
let byte = bytes[i + j];
serialize_escape_byte(byte, result);
i += j + 1;
break;
}
}
if !needs_escape {
let safe_part = unsafe { std::str::from_utf8_unchecked(&bytes[i..i + 8]) };
result.push_str(safe_part);
i += 8;
}
}
while i < bytes.len() {
let escape = ESCAPE[bytes[i] as usize];
if escape != 0 {
serialize_escape_byte(bytes[i], result);
} else {
result.push(unsafe { char::from_u32_unchecked(bytes[i] as u32) });
}
i += 1;
}
result.push('"');
}
#[inline(always)]
fn serialize_escape_byte(byte: u8, result: &mut String) {
match ESCAPE[byte as usize] {
BB => result.push_str("\\b"),
TT => result.push_str("\\t"),
NN => result.push_str("\\n"),
FF => result.push_str("\\f"),
RR => result.push_str("\\r"),
QU => result.push_str("\\\""),
BS => result.push_str("\\\\"),
UU => {
const HEX: &[u8; 16] = b"0123456789abcdef";
result.push_str("\\u00");
result.push(HEX[(byte >> 4) as usize] as char);
result.push(HEX[(byte & 0x0F) as usize] as char);
}
_ => {
result.push(byte as char);
}
}
}
#[inline(always)]
fn serialize_number(n: &Number, result: &mut String) {
if let Some(i) = n.as_i64() {
let mut buffer = itoa::Buffer::new();
result.push_str(buffer.format(i));
} else if let Some(u) = n.as_u64() {
let mut buffer = itoa::Buffer::new();
result.push_str(buffer.format(u));
} else if let Some(f) = n.as_f64() {
if f.fract() == 0.0 {
let mut buffer = itoa::Buffer::new();
result.push_str(buffer.format(f as i64));
} else {
let mut buffer = ryu::Buffer::new();
result.push_str(buffer.format_finite(f));
}
} else {
result.push_str("0");
}
}
fn serialize_pretty_with_depth(
value: &Value,
indent: &str,
depth: usize,
in_array: bool,
result: &mut String,
) {
match value {
Value::Object(map) if map.is_empty() => {
if in_array {
result.push_str("{}");
}
}
Value::Object(map) => serialize_object_pretty(map, indent, depth, in_array, result),
Value::Array(arr) if arr.is_empty() => result.push_str("[]"),
Value::Array(arr) => serialize_array_pretty(arr, indent, depth, result),
Value::String(s) => serialize_string(s, result),
Value::Number(n) => serialize_number(n, result),
Value::Bool(b) => result.push_str(if *b { "true" } else { "false" }),
Value::Null => result.push_str("null"),
}
}
fn serialize_object_pretty(
map: &Map<String, Value>,
indent: &str,
depth: usize,
in_array: bool,
result: &mut String,
) {
let mut keys: Vec<&String> = map.keys().collect();
keys.sort();
if in_array {
for _ in 0..depth + 1 {
result.push_str(indent);
}
result.push_str("{\n");
} else if depth > 0 {
result.push_str("{\n");
}
let mut first = true;
for key in keys {
if !first {
result.push_str(",\n");
}
first = false;
let inner_indent = if in_array {
depth + 2
} else if depth == 0 {
0
} else {
depth
};
for _ in 0..inner_indent {
result.push_str(indent);
}
serialize_key(key, result);
result.push_str(" = ");
let value = &map[key];
serialize_pretty_with_depth(value, indent, depth + 1, false, result);
}
if in_array {
result.push('\n');
for _ in 0..depth + 1 {
result.push_str(indent);
}
result.push('}');
} else if depth > 0 {
result.push('\n');
for _ in 0..depth - 1 {
result.push_str(indent);
}
result.push('}');
}
}
fn serialize_array_pretty(arr: &[Value], indent: &str, depth: usize, result: &mut String) {
result.push_str("[\n");
let mut first = true;
for value in arr {
if !first {
result.push_str(",\n");
}
first = false;
if matches!(value, Value::Object(_)) {
serialize_pretty_with_depth(value, indent, depth, true, result);
} else {
for _ in 0..depth + 1 {
result.push_str(indent);
}
serialize_pretty_with_depth(value, indent, depth + 1, false, result);
}
}
result.push('\n');
for _ in 0..depth {
result.push_str(indent);
}
result.push(']');
}
fn needs_quoting(s: &str) -> bool {
if s.is_empty() {
return true;
}
!s.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-')
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_empty_input() {
let result = parse("").unwrap();
assert_eq!(result, json!({}));
}
#[test]
fn test_basic_key_value() {
let result = parse(r#"a="hello", b=123.45"#).unwrap();
assert_eq!(
result,
json!({
"a": "hello",
"b": 123.45
})
);
}
#[test]
fn test_string_types() {
let result = parse(r#""quoted key"="value", unquoted_key="another""#).unwrap();
assert_eq!(
result,
json!({
"quoted key": "value",
"unquoted_key": "another"
})
);
}
#[test]
fn test_numbers() {
let result = parse(r#"int=42, float=3.14, negative=-123, negative_float=-45.67"#).unwrap();
assert_eq!(
result,
json!({
"int": 42,
"float": 3.14,
"negative": -123,
"negative_float": -45.67
})
);
}
#[test]
fn test_numbers_with_underscores() {
let result = parse(
r#"large=100_000, million=1_000_000, decimal=1_234.567_890, neg_large=-50_000"#,
)
.unwrap();
assert_eq!(
result,
json!({
"large": 100_000,
"million": 1_000_000,
"decimal": 1_234.567_890,
"neg_large": -50_000
})
);
}
#[test]
fn test_booleans() {
let result = parse(r#"truth=true, falsehood=false"#).unwrap();
assert_eq!(
result,
json!({
"truth": true,
"falsehood": false
})
);
}
#[test]
fn test_null_value() {
let result = parse(r#"empty=null"#).unwrap();
assert_eq!(result, json!({"empty": null}));
}
#[test]
fn test_arrays_with_strings() {
let result = parse(r#"strings=["hello", "world", "test"]"#).unwrap();
assert_eq!(
result,
json!({
"strings": ["hello", "world", "test"]
})
);
}
#[test]
fn test_arrays_with_numbers() {
let result = parse(r#"numbers=[1, 2.5, -3, 4.0]"#).unwrap();
assert_eq!(
result,
json!({
"numbers": [1, 2.5, -3, 4.0]
})
);
}
#[test]
fn test_nested_objects() {
let result = parse(r#"server={host="localhost", port=8080}"#).unwrap();
assert_eq!(
result,
json!({
"server": {
"host": "localhost",
"port": 8080
}
})
);
}
#[test]
fn test_raw_strings() {
let result = parse(r###"path=r"C:\Windows\System32""###).unwrap();
assert_eq!(result, json!({"path": r"C:\Windows\System32"}));
let result2 = parse(r###"quote=r#"He said "hello" to me"#"###).unwrap();
assert_eq!(result2["quote"], r#"He said "hello" to me"#);
}
#[test]
fn test_serialize_basic_object() {
let value = json!({"name": "John", "age": 30});
let result = serialize(&value);
assert_eq!(result, r#"age=30,name="John""#);
}
#[test]
fn test_serialize_nested_object() {
let value = json!({"server": {"host": "localhost", "port": 8080.0}});
let result = serialize(&value);
assert_eq!(result, r#"server={host="localhost",port=8080}"#);
}
#[test]
fn test_serialize_array_with_objects() {
let value = json!([{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]);
let result = serialize(&value);
assert_eq!(result, r#"[{age=30,name="John"},{age=25,name="Jane"}]"#);
}
#[test]
fn test_serialize_round_trip_simple() {
let original = json!({"name": "John", "age": 30, "active": true});
let serialized = serialize(&original);
let parsed = parse(&serialized).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_serialize_pretty_basic_object() {
let value = json!({"name": "John", "age": 30});
let result = serialize_pretty(&value, " ");
assert_eq!(result, "age = 30,\nname = \"John\"");
}
#[test]
fn test_serialize_pretty_nested_objects() {
let value = json!({"server": {"host": "localhost", "port": 8080}});
let result = serialize_pretty(&value, " ");
assert_eq!(
result,
"server = {\n host = \"localhost\",\n port = 8080\n}"
);
}
#[test]
fn test_serialize_pretty_array() {
let value = json!([1, 2, 3, "hello"]);
let result = serialize_pretty(&value, " ");
assert_eq!(result, "[\n 1,\n 2,\n 3,\n \"hello\"\n]");
}
#[test]
fn test_serialize_pretty_array_with_objects() {
let value = json!([{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]);
let result = serialize_pretty(&value, " ");
assert_eq!(
result,
"[\n {\n age = 30,\n name = \"John\"\n },\n {\n age = 25,\n name = \"Jane\"\n }\n]"
);
}
#[test]
fn test_serialize_pretty_round_trip() {
let original = json!({
"name": "John",
"age": 30,
"active": true,
"tags": ["developer", "rust"]
});
let serialized = serialize_pretty(&original, " ");
let parsed = parse(&serialized).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_serde_serialize_struct() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
name: String,
age: u32,
active: bool,
}
let config = Config {
name: "John".to_string(),
age: 30,
active: true,
};
let jhon_string = to_string(&config).unwrap();
assert_eq!(jhon_string, r#"active=true,age=30,name="John""#);
}
#[test]
fn test_serde_deserialize_struct() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
name: String,
age: u32,
active: bool,
}
let jhon_str = r#"name="John",age=30,active=true"#;
let config: Config = from_str(jhon_str).unwrap();
assert_eq!(config.name, "John");
assert_eq!(config.age, 30);
assert_eq!(config.active, true);
}
#[test]
fn test_serde_round_trip_struct() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Person {
name: String,
age: u32,
email: String,
}
let original = Person {
name: "Alice".to_string(),
age: 25,
email: "alice@example.com".to_string(),
};
let serialized = to_string(&original).unwrap();
let deserialized: Person = from_str(&serialized).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_serde_serialize_nested_struct() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct ServerConfig {
host: String,
port: u16,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct AppConfig {
name: String,
server: ServerConfig,
}
let config = AppConfig {
name: "MyApp".to_string(),
server: ServerConfig {
host: "localhost".to_string(),
port: 8080,
},
};
let jhon_string = to_string(&config).unwrap();
assert_eq!(jhon_string, r#"name="MyApp",server={host="localhost",port=8080}"#);
}
#[test]
fn test_serde_deserialize_nested_struct() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct ServerConfig {
host: String,
port: u16,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct AppConfig {
name: String,
server: ServerConfig,
}
let jhon_str = r#"name="MyApp",server={host="localhost",port=8080}"#;
let config: AppConfig = from_str(jhon_str).unwrap();
assert_eq!(config.name, "MyApp");
assert_eq!(config.server.host, "localhost");
assert_eq!(config.server.port, 8080);
}
#[test]
fn test_serde_serialize_with_arrays() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
tags: Vec<String>,
scores: Vec<i32>,
}
let config = Config {
tags: vec!["rust".to_string(), "web".to_string()],
scores: vec![100, 95, 88],
};
let jhon_string = to_string(&config).unwrap();
assert_eq!(jhon_string, r#"scores=[100,95,88],tags=["rust","web"]"#);
}
#[test]
fn test_serde_deserialize_with_arrays() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
tags: Vec<String>,
scores: Vec<i32>,
}
let jhon_str = r#"tags=["rust","web"],scores=[100,95,88]"#;
let config: Config = from_str(jhon_str).unwrap();
assert_eq!(config.tags, vec!["rust", "web"]);
assert_eq!(config.scores, vec![100, 95, 88]);
}
#[test]
fn test_serde_serialize_with_option() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
name: String,
description: Option<String>,
}
let config = Config {
name: "Test".to_string(),
description: Some("A test config".to_string()),
};
let jhon_string = to_string(&config).unwrap();
assert_eq!(jhon_string, r#"description="A test config",name="Test""#);
}
#[test]
fn test_serde_deserialize_with_option_none() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
name: String,
description: Option<String>,
}
let jhon_str = r#"name="Test""#;
let config: Config = from_str(jhon_str).unwrap();
assert_eq!(config.name, "Test");
assert_eq!(config.description, None);
}
#[test]
fn test_jhon_wrapper_to_string() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 10, y: 20 };
let jhon_string = Jhon::to_string(&point).unwrap();
assert_eq!(jhon_string, r#"x=10,y=20"#);
}
#[test]
fn test_jhon_wrapper_from_str() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Point {
x: i32,
y: i32,
}
let jhon_str = r#"x=10,y=20"#;
let point: Point = Jhon::from_str(jhon_str).unwrap();
assert_eq!(point.x, 10);
assert_eq!(point.y, 20);
}
#[test]
fn test_serde_pretty_print() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
name: String,
age: u32,
}
let config = Config {
name: "John".to_string(),
age: 30,
};
let jhon_string = to_string_pretty(&config, " ").unwrap();
assert_eq!(jhon_string, "age = 30,\nname = \"John\"");
}
#[test]
fn test_serde_with_enum() {
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Status {
Active,
Inactive,
Pending,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Task {
name: String,
status: Status,
}
let task = Task {
name: "Task1".to_string(),
status: Status::Active,
};
let jhon_string = to_string(&task).unwrap();
assert_eq!(jhon_string, r#"name="Task1",status="Active""#);
let decoded: Task = from_str(&jhon_string).unwrap();
assert_eq!(decoded, task);
}
#[test]
fn test_all_original_tests_still_pass() {
let result = parse(r#"a="hello", b=123.45"#).unwrap();
assert_eq!(
result,
json!({
"a": "hello",
"b": 123.45
})
);
}
}