mirror of
https://github.com/perstarkse/minne.git
synced 2026-05-28 10:29:30 +02:00
refactor: json-stream-parser aligned to clippy standard
This commit is contained in:
Generated
+1
@@ -3533,6 +3533,7 @@ name = "json-stream-parser"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
|||||||
+194
-167
@@ -1,6 +1,7 @@
|
|||||||
// This code is based on the json-stream-rust library (https://github.com/json-stream/json-stream-rust)
|
// This code is based on the json-stream-rust library (https://github.com/json-stream/json-stream-rust)
|
||||||
// Original code is MIT licensed
|
// Original code is MIT licensed
|
||||||
// Modified to fix escape character handling in strings
|
// Modified to fix escape character handling in strings
|
||||||
|
use std::mem;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -9,107 +10,122 @@ enum ObjectStatus {
|
|||||||
StringQuoteOpen(bool),
|
StringQuoteOpen(bool),
|
||||||
StringQuoteClose,
|
StringQuoteClose,
|
||||||
Scalar {
|
Scalar {
|
||||||
value_so_far: Vec<char>,
|
value_so_far: String,
|
||||||
},
|
},
|
||||||
ScalarNumber {
|
ScalarNumber {
|
||||||
value_so_far: Vec<char>,
|
value_so_far: String,
|
||||||
},
|
},
|
||||||
StartProperty,
|
StartProperty,
|
||||||
KeyQuoteOpen {
|
KeyQuoteOpen {
|
||||||
key_so_far: Vec<char>,
|
key_so_far: String,
|
||||||
escaped: bool,
|
escaped: bool,
|
||||||
},
|
},
|
||||||
KeyQuoteClose {
|
KeyQuoteClose {
|
||||||
key: Vec<char>,
|
key: String,
|
||||||
},
|
},
|
||||||
Colon {
|
Colon {
|
||||||
key: Vec<char>,
|
key: String,
|
||||||
},
|
},
|
||||||
ValueQuoteOpen {
|
ValueQuoteOpen {
|
||||||
key: Vec<char>,
|
key: String,
|
||||||
escaped: bool,
|
escaped: bool,
|
||||||
},
|
},
|
||||||
ValueQuoteClose,
|
ValueQuoteClose,
|
||||||
ValueScalar {
|
ValueScalar {
|
||||||
key: Vec<char>,
|
key: String,
|
||||||
value_so_far: Vec<char>,
|
value_so_far: String,
|
||||||
},
|
},
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors that can occur during streaming JSON parsing.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ParseError {
|
||||||
|
/// A key that was expected to have a string value has a different type.
|
||||||
|
#[error("invalid value type for key `{0}`")]
|
||||||
|
InvalidValueType(String),
|
||||||
|
/// A numeric literal could not be parsed at the top level.
|
||||||
|
#[error("invalid number: {0}")]
|
||||||
|
InvalidNumber(String),
|
||||||
|
/// A scalar value inside an object could not be parsed as JSON.
|
||||||
|
#[error("invalid value for key `{0}`: {1}")]
|
||||||
|
InvalidObjectValue(String, String),
|
||||||
|
/// A character was encountered that does not fit the expected state.
|
||||||
|
#[error("unexpected character `{char}`")]
|
||||||
|
UnexpectedCharacter {
|
||||||
|
char: char,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
fn add_char_into_object(
|
fn add_char_into_object(
|
||||||
object: &mut Value,
|
object: &mut Value,
|
||||||
current_status: &mut ObjectStatus,
|
current_status: &mut ObjectStatus,
|
||||||
current_char: char,
|
current_char: char,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), ParseError> {
|
||||||
match (&*object, &*current_status, current_char) {
|
match (&*object, &*current_status, current_char) {
|
||||||
|
// --- Top-level string value ---
|
||||||
(&Value::String(_), &ObjectStatus::StringQuoteOpen(true), '"') => {
|
(&Value::String(_), &ObjectStatus::StringQuoteOpen(true), '"') => {
|
||||||
if let Value::String(str) = object {
|
if let Value::String(s) = object {
|
||||||
str.push('"');
|
s.push('"');
|
||||||
|
}
|
||||||
|
if let ObjectStatus::StringQuoteOpen(ref mut escaped) = current_status {
|
||||||
|
*escaped = false;
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::StringQuoteOpen(false);
|
|
||||||
}
|
}
|
||||||
(&Value::String(_), &ObjectStatus::StringQuoteOpen(false), '"') => {
|
(&Value::String(_), &ObjectStatus::StringQuoteOpen(false), '"') => {
|
||||||
*current_status = ObjectStatus::StringQuoteClose;
|
*current_status = ObjectStatus::StringQuoteClose;
|
||||||
}
|
}
|
||||||
(&Value::String(_), &ObjectStatus::StringQuoteOpen(true), c) => {
|
(&Value::String(_), &ObjectStatus::StringQuoteOpen(true), c) => {
|
||||||
if let Value::String(str) = object {
|
if let Value::String(s) = object {
|
||||||
str.push('\\');
|
s.push('\\');
|
||||||
str.push(c);
|
s.push(c);
|
||||||
|
}
|
||||||
|
if let ObjectStatus::StringQuoteOpen(ref mut escaped) = current_status {
|
||||||
|
*escaped = false;
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::StringQuoteOpen(false);
|
|
||||||
}
|
}
|
||||||
(&Value::String(_), &ObjectStatus::StringQuoteOpen(false), '\\') => {
|
(&Value::String(_), &ObjectStatus::StringQuoteOpen(false), '\\') => {
|
||||||
*current_status = ObjectStatus::StringQuoteOpen(true);
|
if let ObjectStatus::StringQuoteOpen(ref mut escaped) = current_status {
|
||||||
|
*escaped = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(&Value::String(_), &ObjectStatus::StringQuoteOpen(false), c) => {
|
(&Value::String(_), &ObjectStatus::StringQuoteOpen(false), c) => {
|
||||||
if let Value::String(str) = object {
|
if let Value::String(s) = object {
|
||||||
str.push(c);
|
s.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Object: key with escaped quote ---
|
||||||
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: true, .. }, '"') => {
|
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: true, .. }, '"') => {
|
||||||
if let ObjectStatus::KeyQuoteOpen {
|
if let ObjectStatus::KeyQuoteOpen { ref mut key_so_far, ref mut escaped } =
|
||||||
ref mut key_so_far, ..
|
current_status
|
||||||
} = current_status
|
|
||||||
{
|
{
|
||||||
key_so_far.push('"');
|
key_so_far.push('"');
|
||||||
*current_status = ObjectStatus::KeyQuoteOpen {
|
*escaped = false;
|
||||||
key_so_far: key_so_far.clone(),
|
|
||||||
escaped: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: false, .. }, '"') => {
|
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: false, .. }, '"') => {
|
||||||
if let ObjectStatus::KeyQuoteOpen { ref key_so_far, .. } = current_status {
|
if let ObjectStatus::KeyQuoteOpen { ref mut key_so_far, .. } = current_status {
|
||||||
let key = key_so_far.iter().collect::<String>();
|
let key = mem::take(key_so_far);
|
||||||
if let Value::Object(obj) = object {
|
if let Value::Object(obj) = object {
|
||||||
obj.insert(key, Value::Null);
|
obj.insert(key.clone(), Value::Null);
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::KeyQuoteClose {
|
*current_status = ObjectStatus::KeyQuoteClose { key };
|
||||||
key: key_so_far.clone(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// --- Object: key with escaped other char ---
|
||||||
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: true, .. }, c) => {
|
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: true, .. }, c) => {
|
||||||
if let ObjectStatus::KeyQuoteOpen {
|
if let ObjectStatus::KeyQuoteOpen { ref mut key_so_far, ref mut escaped } =
|
||||||
ref mut key_so_far, ..
|
current_status
|
||||||
} = current_status
|
|
||||||
{
|
{
|
||||||
key_so_far.push('\\');
|
key_so_far.push('\\');
|
||||||
key_so_far.push(c);
|
key_so_far.push(c);
|
||||||
*current_status = ObjectStatus::KeyQuoteOpen {
|
*escaped = false;
|
||||||
key_so_far: key_so_far.clone(),
|
|
||||||
escaped: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: false, .. }, '\\') => {
|
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: false, .. }, '\\') => {
|
||||||
if let ObjectStatus::KeyQuoteOpen { ref key_so_far, .. } = current_status {
|
if let ObjectStatus::KeyQuoteOpen { ref mut escaped, .. } = current_status {
|
||||||
*current_status = ObjectStatus::KeyQuoteOpen {
|
*escaped = true;
|
||||||
key_so_far: key_so_far.clone(),
|
|
||||||
escaped: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: false, .. }, c) => {
|
(&Value::Object(_), &ObjectStatus::KeyQuoteOpen { escaped: false, .. }, c) => {
|
||||||
@@ -121,59 +137,49 @@ fn add_char_into_object(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Object: value with escaped quote ---
|
||||||
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: true, .. }, '"') => {
|
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: true, .. }, '"') => {
|
||||||
if let ObjectStatus::ValueQuoteOpen { ref key, .. } = current_status {
|
if let ObjectStatus::ValueQuoteOpen { ref key, ref mut escaped } = current_status {
|
||||||
let key_str = key.iter().collect::<String>();
|
|
||||||
if let Value::Object(obj) = object {
|
if let Value::Object(obj) = object {
|
||||||
if let Some(Value::String(value)) = obj.get_mut(&key_str) {
|
if let Some(Value::String(value)) = obj.get_mut(key) {
|
||||||
value.push('"');
|
value.push('"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::ValueQuoteOpen {
|
*escaped = false;
|
||||||
key: key.clone(),
|
|
||||||
escaped: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: false, .. }, '"') => {
|
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: false, .. }, '"') => {
|
||||||
*current_status = ObjectStatus::ValueQuoteClose;
|
*current_status = ObjectStatus::ValueQuoteClose;
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: true, .. }, c) => {
|
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: true, .. }, c) => {
|
||||||
if let ObjectStatus::ValueQuoteOpen { ref key, .. } = current_status {
|
if let ObjectStatus::ValueQuoteOpen { ref key, ref mut escaped } = current_status {
|
||||||
let key_str = key.iter().collect::<String>();
|
|
||||||
if let Value::Object(obj) = object {
|
if let Value::Object(obj) = object {
|
||||||
if let Some(Value::String(value)) = obj.get_mut(&key_str) {
|
if let Some(Value::String(value)) = obj.get_mut(key) {
|
||||||
value.push('\\');
|
value.push('\\');
|
||||||
value.push(c);
|
value.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::ValueQuoteOpen {
|
*escaped = false;
|
||||||
key: key.clone(),
|
|
||||||
escaped: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: false, .. }, '\\') => {
|
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: false, .. }, '\\') => {
|
||||||
if let ObjectStatus::ValueQuoteOpen { ref key, .. } = current_status {
|
if let ObjectStatus::ValueQuoteOpen { ref mut escaped, .. } = current_status {
|
||||||
*current_status = ObjectStatus::ValueQuoteOpen {
|
*escaped = true;
|
||||||
key: key.clone(),
|
|
||||||
escaped: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: false, .. }, c) => {
|
(&Value::Object(_), &ObjectStatus::ValueQuoteOpen { escaped: false, .. }, c) => {
|
||||||
if let ObjectStatus::ValueQuoteOpen { ref key, .. } = current_status {
|
if let ObjectStatus::ValueQuoteOpen { ref key, .. } = current_status {
|
||||||
let key_str = key.iter().collect::<String>();
|
|
||||||
if let Value::Object(obj) = object {
|
if let Value::Object(obj) = object {
|
||||||
if let Some(Value::String(value)) = obj.get_mut(&key_str) {
|
if let Some(Value::String(value)) = obj.get_mut(key) {
|
||||||
value.push(c);
|
value.push(c);
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Invalid value type for key {}", key_str));
|
return Err(ParseError::InvalidValueType(key.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Top-level init ---
|
||||||
(&Value::Null, &ObjectStatus::Ready, '"') => {
|
(&Value::Null, &ObjectStatus::Ready, '"') => {
|
||||||
*object = json!("");
|
*object = json!("");
|
||||||
*current_status = ObjectStatus::StringQuoteOpen(false);
|
*current_status = ObjectStatus::StringQuoteOpen(false);
|
||||||
@@ -186,7 +192,7 @@ fn add_char_into_object(
|
|||||||
(&Value::Null, &ObjectStatus::Ready, 't') => {
|
(&Value::Null, &ObjectStatus::Ready, 't') => {
|
||||||
*object = json!(true);
|
*object = json!(true);
|
||||||
*current_status = ObjectStatus::Scalar {
|
*current_status = ObjectStatus::Scalar {
|
||||||
value_so_far: vec!['t'],
|
value_so_far: "t".into(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(&Value::Bool(true), &ObjectStatus::Scalar { .. }, 'r') => {
|
(&Value::Bool(true), &ObjectStatus::Scalar { .. }, 'r') => {
|
||||||
@@ -194,7 +200,7 @@ fn add_char_into_object(
|
|||||||
ref mut value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
if *value_so_far == vec!['t'] {
|
if *value_so_far == "t" {
|
||||||
value_so_far.push('r');
|
value_so_far.push('r');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,19 +210,21 @@ fn add_char_into_object(
|
|||||||
ref mut value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
if *value_so_far == vec!['t', 'r'] {
|
if *value_so_far == "tr" {
|
||||||
value_so_far.push('u');
|
value_so_far.push('u');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Bool(true), &ObjectStatus::Scalar { .. }, 'e') => {
|
(&Value::Bool(true), &ObjectStatus::Scalar { .. }, 'e')
|
||||||
|
| (&Value::Bool(false), &ObjectStatus::Scalar { .. }, 'e')
|
||||||
|
| (&Value::Object(_), &ObjectStatus::ValueQuoteClose, '}') => {
|
||||||
*current_status = ObjectStatus::Closed;
|
*current_status = ObjectStatus::Closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
(&Value::Null, &ObjectStatus::Ready, 'f') => {
|
(&Value::Null, &ObjectStatus::Ready, 'f') => {
|
||||||
*object = json!(false);
|
*object = json!(false);
|
||||||
*current_status = ObjectStatus::Scalar {
|
*current_status = ObjectStatus::Scalar {
|
||||||
value_so_far: vec!['f'],
|
value_so_far: "f".into(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(&Value::Bool(false), &ObjectStatus::Scalar { .. }, 'a') => {
|
(&Value::Bool(false), &ObjectStatus::Scalar { .. }, 'a') => {
|
||||||
@@ -224,7 +232,7 @@ fn add_char_into_object(
|
|||||||
ref mut value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
if *value_so_far == vec!['f'] {
|
if *value_so_far == "f" {
|
||||||
value_so_far.push('a');
|
value_so_far.push('a');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,7 +242,7 @@ fn add_char_into_object(
|
|||||||
ref mut value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
if *value_so_far == vec!['f', 'a'] {
|
if *value_so_far == "fa" {
|
||||||
value_so_far.push('l');
|
value_so_far.push('l');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,19 +252,16 @@ fn add_char_into_object(
|
|||||||
ref mut value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
if *value_so_far == vec!['f', 'a', 'l'] {
|
if *value_so_far == "fal" {
|
||||||
value_so_far.push('s');
|
value_so_far.push('s');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Bool(false), &ObjectStatus::Scalar { .. }, 'e') => {
|
|
||||||
*current_status = ObjectStatus::Closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Value::Null, &ObjectStatus::Ready, 'n') => {
|
(&Value::Null, &ObjectStatus::Ready, 'n') => {
|
||||||
*object = json!(null);
|
*object = json!(null);
|
||||||
*current_status = ObjectStatus::Scalar {
|
*current_status = ObjectStatus::Scalar {
|
||||||
value_so_far: vec!['n'],
|
value_so_far: "n".into(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(&Value::Null, &ObjectStatus::Scalar { .. }, 'u') => {
|
(&Value::Null, &ObjectStatus::Scalar { .. }, 'u') => {
|
||||||
@@ -264,7 +269,7 @@ fn add_char_into_object(
|
|||||||
ref mut value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
if *value_so_far == vec!['n'] {
|
if *value_so_far == "n" {
|
||||||
value_so_far.push('u');
|
value_so_far.push('u');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,24 +279,27 @@ fn add_char_into_object(
|
|||||||
ref mut value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
if *value_so_far == vec!['n', 'u'] {
|
if *value_so_far == "nu" {
|
||||||
value_so_far.push('l');
|
value_so_far.push('l');
|
||||||
} else if *value_so_far == vec!['n', 'u', 'l'] {
|
} else if *value_so_far == "nul" {
|
||||||
*current_status = ObjectStatus::Closed;
|
*current_status = ObjectStatus::Closed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Numbers ---
|
||||||
(&Value::Null, &ObjectStatus::Ready, c @ '0'..='9') => {
|
(&Value::Null, &ObjectStatus::Ready, c @ '0'..='9') => {
|
||||||
*object = Value::Number(c.to_digit(10).unwrap().into());
|
if let Some(digit) = c.to_digit(10) {
|
||||||
|
*object = Value::Number(digit.into());
|
||||||
|
}
|
||||||
*current_status = ObjectStatus::ScalarNumber {
|
*current_status = ObjectStatus::ScalarNumber {
|
||||||
value_so_far: vec![c],
|
value_so_far: c.to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(&Value::Null, &ObjectStatus::Ready, '-') => {
|
(&Value::Null, &ObjectStatus::Ready, '-') => {
|
||||||
*object = Value::Number(0.into());
|
*object = Value::Number(0.into());
|
||||||
*current_status = ObjectStatus::ScalarNumber {
|
*current_status = ObjectStatus::ScalarNumber {
|
||||||
value_so_far: vec!['-'],
|
value_so_far: "-".into(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(&Value::Number(_), &ObjectStatus::ScalarNumber { .. }, c @ '0'..='9') => {
|
(&Value::Number(_), &ObjectStatus::ScalarNumber { .. }, c @ '0'..='9') => {
|
||||||
@@ -301,22 +309,22 @@ fn add_char_into_object(
|
|||||||
{
|
{
|
||||||
value_so_far.push(c);
|
value_so_far.push(c);
|
||||||
if let Value::Number(ref mut num) = object {
|
if let Value::Number(ref mut num) = object {
|
||||||
if value_so_far.contains(&'.') {
|
if value_so_far.contains('.') {
|
||||||
let parsed_number = value_so_far
|
let parsed: f64 = value_so_far.parse().map_err(|e| {
|
||||||
.iter()
|
ParseError::InvalidNumber(format!(
|
||||||
.collect::<String>()
|
"invalid float: `{value_so_far}` ({e})"
|
||||||
.parse::<f64>()
|
))
|
||||||
.unwrap();
|
})?;
|
||||||
if let Some(json_number) = serde_json::Number::from_f64(parsed_number) {
|
if let Some(json_number) = serde_json::Number::from_f64(parsed) {
|
||||||
*num = json_number;
|
*num = json_number;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let parsed_number = value_so_far
|
let parsed: i64 = value_so_far.parse().map_err(|e| {
|
||||||
.iter()
|
ParseError::InvalidNumber(format!(
|
||||||
.collect::<String>()
|
"invalid integer: `{value_so_far}` ({e})"
|
||||||
.parse::<i64>()
|
))
|
||||||
.unwrap();
|
})?;
|
||||||
*num = parsed_number.into();
|
*num = parsed.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,53 +338,58 @@ fn add_char_into_object(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Object structure ---
|
||||||
(&Value::Object(_), &ObjectStatus::StartProperty, '"') => {
|
(&Value::Object(_), &ObjectStatus::StartProperty, '"') => {
|
||||||
*current_status = ObjectStatus::KeyQuoteOpen {
|
*current_status = ObjectStatus::KeyQuoteOpen {
|
||||||
key_so_far: vec![],
|
key_so_far: String::new(),
|
||||||
escaped: false,
|
escaped: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::KeyQuoteClose { .. }, ':') => {
|
(&Value::Object(_), &ObjectStatus::KeyQuoteClose { .. }, ':') => {
|
||||||
if let ObjectStatus::KeyQuoteClose { ref key } = current_status {
|
if let ObjectStatus::KeyQuoteClose { ref mut key } = current_status {
|
||||||
*current_status = ObjectStatus::Colon { key: key.clone() };
|
let key = mem::take(key);
|
||||||
|
*current_status = ObjectStatus::Colon { key };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::Colon { .. }, ' ' | '\n') => {}
|
(&Value::Object(_), &ObjectStatus::Colon { .. }, ' ' | '\n' | '\t' | '\r') => {}
|
||||||
(&Value::Object(_), &ObjectStatus::Colon { .. }, '"') => {
|
(&Value::Object(_), &ObjectStatus::Colon { .. }, '"') => {
|
||||||
if let ObjectStatus::Colon { ref key } = current_status {
|
if let ObjectStatus::Colon { ref mut key } = current_status {
|
||||||
let key_str = key.iter().collect::<String>();
|
let key_str = mem::take(key);
|
||||||
if let Value::Object(obj) = object {
|
if let Value::Object(obj) = object {
|
||||||
obj.insert(key_str, json!(""));
|
obj.insert(key_str.clone(), json!(""));
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::ValueQuoteOpen {
|
*current_status = ObjectStatus::ValueQuoteOpen {
|
||||||
key: key.clone(),
|
key: key_str,
|
||||||
escaped: false,
|
escaped: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(&Value::Object(_), &ObjectStatus::Colon { .. }, char) => {
|
(&Value::Object(_), &ObjectStatus::Colon { .. }, char) => {
|
||||||
if let ObjectStatus::Colon { ref key } = current_status {
|
if let ObjectStatus::Colon { ref mut key } = current_status {
|
||||||
|
let key = mem::take(key);
|
||||||
*current_status = ObjectStatus::ValueScalar {
|
*current_status = ObjectStatus::ValueScalar {
|
||||||
key: key.clone(),
|
key,
|
||||||
value_so_far: vec![char],
|
value_so_far: char.to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::ValueScalar { .. }, ',') => {
|
(&Value::Object(_), &ObjectStatus::ValueScalar { .. }, ',') => {
|
||||||
if let ObjectStatus::ValueScalar {
|
if let ObjectStatus::ValueScalar {
|
||||||
ref key,
|
ref mut key,
|
||||||
ref value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
let key_str = key.iter().collect::<String>();
|
let key = mem::take(key);
|
||||||
let value_str = value_so_far.iter().collect::<String>();
|
let value_str = mem::take(value_so_far);
|
||||||
if let Value::Object(obj) = object {
|
if let Value::Object(obj) = object {
|
||||||
match value_str.parse::<Value>() {
|
match value_str.parse::<Value>() {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
obj.insert(key_str, value);
|
obj.insert(key, value);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(ParseError::InvalidObjectValue(key, e.to_string()));
|
||||||
}
|
}
|
||||||
Err(e) => return Err(format!("Invalid value for key {}: {}", key_str, e)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::StartProperty;
|
*current_status = ObjectStatus::StartProperty;
|
||||||
@@ -384,18 +397,20 @@ fn add_char_into_object(
|
|||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::ValueScalar { .. }, '}') => {
|
(&Value::Object(_), &ObjectStatus::ValueScalar { .. }, '}') => {
|
||||||
if let ObjectStatus::ValueScalar {
|
if let ObjectStatus::ValueScalar {
|
||||||
ref key,
|
ref mut key,
|
||||||
ref value_so_far,
|
ref mut value_so_far,
|
||||||
} = current_status
|
} = current_status
|
||||||
{
|
{
|
||||||
let key_str = key.iter().collect::<String>();
|
let key = mem::take(key);
|
||||||
let value_str = value_so_far.iter().collect::<String>();
|
let value_str = mem::take(value_so_far);
|
||||||
if let Value::Object(obj) = object {
|
if let Value::Object(obj) = object {
|
||||||
match value_str.parse::<Value>() {
|
match value_str.parse::<Value>() {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
obj.insert(key_str, value);
|
obj.insert(key, value);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(ParseError::InvalidObjectValue(key, e.to_string()));
|
||||||
}
|
}
|
||||||
Err(e) => return Err(format!("Invalid value for key {}: {}", key_str, e)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*current_status = ObjectStatus::Closed;
|
*current_status = ObjectStatus::Closed;
|
||||||
@@ -414,54 +429,54 @@ fn add_char_into_object(
|
|||||||
(&Value::Object(_), &ObjectStatus::ValueQuoteClose, ',') => {
|
(&Value::Object(_), &ObjectStatus::ValueQuoteClose, ',') => {
|
||||||
*current_status = ObjectStatus::StartProperty;
|
*current_status = ObjectStatus::StartProperty;
|
||||||
}
|
}
|
||||||
(&Value::Object(_), &ObjectStatus::ValueQuoteClose, '}') => {
|
(_, _, ' ' | '\n' | '\t' | '\r') => {}
|
||||||
*current_status = ObjectStatus::Closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, _, ' ' | '\n') => {}
|
(_, _, c) => {
|
||||||
|
return Err(ParseError::UnexpectedCharacter { char: c });
|
||||||
(val, st, c) => {
|
|
||||||
return Err(format!(
|
|
||||||
"Invalid character {} status: {:?} value: {:?}",
|
|
||||||
c, st, val
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
/// Parse a complete JSON string in one shot.
|
||||||
pub fn parse_stream(json_string: &str) -> Result<Value, String> {
|
///
|
||||||
|
/// Processes the input character-by-character through the streaming parser state machine.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`ParseError`] if the input contains invalid JSON or unexpected characters
|
||||||
|
/// for the current parser state.
|
||||||
|
#[must_use]
|
||||||
|
pub fn parse_stream(json_string: &str) -> Result<Value, ParseError> {
|
||||||
let mut out: Value = Value::Null;
|
let mut out: Value = Value::Null;
|
||||||
let mut current_status = ObjectStatus::Ready;
|
let mut current_status = ObjectStatus::Ready;
|
||||||
for current_char in json_string.chars() {
|
for current_char in json_string.chars() {
|
||||||
println!(
|
add_char_into_object(&mut out, &mut current_status, current_char)?;
|
||||||
"variables: {:?} {:?} {:?}",
|
|
||||||
out,
|
|
||||||
current_status.clone(),
|
|
||||||
current_char.to_string()
|
|
||||||
);
|
|
||||||
add_char_into_object(&mut out, &mut current_status, current_char)?
|
|
||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
/// An incremental streaming JSON parser that processes one character at a time.
|
||||||
pub fn parse_stream(json_string: &str) -> Result<Value, String> {
|
///
|
||||||
let mut out: Value = Value::Null;
|
/// Create via [`JsonStreamParser::new()`], feed characters with [`add_char`](Self::add_char),
|
||||||
let mut current_status = ObjectStatus::Ready;
|
/// and retrieve the result with [`result`](Self::result).
|
||||||
for current_char in json_string.chars() {
|
///
|
||||||
if let Err(e) = add_char_into_object(&mut out, &mut current_status, current_char) {
|
/// # Example
|
||||||
return Err(e);
|
///
|
||||||
}
|
/// ```
|
||||||
}
|
/// # use json_stream_parser::JsonStreamParser;
|
||||||
return Ok(out);
|
/// let mut parser = JsonStreamParser::new();
|
||||||
}
|
/// for c in r#"{"key": "value"}"#.chars() {
|
||||||
|
/// parser.add_char(c).unwrap();
|
||||||
|
/// }
|
||||||
|
/// let result = parser.result();
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct JsonStreamParser {
|
pub struct JsonStreamParser {
|
||||||
object: Value,
|
object: Value,
|
||||||
current_status: ObjectStatus,
|
current_status: ObjectStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for JsonStreamParser {
|
impl Default for JsonStreamParser {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
@@ -469,16 +484,27 @@ impl Default for JsonStreamParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl JsonStreamParser {
|
impl JsonStreamParser {
|
||||||
|
/// Create a new streaming parser in the initial state.
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> JsonStreamParser {
|
pub fn new() -> JsonStreamParser {
|
||||||
JsonStreamParser {
|
JsonStreamParser {
|
||||||
object: Value::Null,
|
object: Value::Null,
|
||||||
current_status: ObjectStatus::Ready,
|
current_status: ObjectStatus::Ready,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_char(&mut self, current_char: char) -> Result<(), String> {
|
|
||||||
|
/// Feed one character into the parser.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`ParseError`] if the character is invalid for the current state.
|
||||||
|
pub fn add_char(&mut self, current_char: char) -> Result<(), ParseError> {
|
||||||
add_char_into_object(&mut self.object, &mut self.current_status, current_char)
|
add_char_into_object(&mut self.object, &mut self.current_status, current_char)
|
||||||
}
|
}
|
||||||
pub fn get_result(&self) -> &Value {
|
|
||||||
|
/// Borrow the current parsed value.
|
||||||
|
#[must_use]
|
||||||
|
pub fn result(&self) -> &Value {
|
||||||
&self.object
|
&self.object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -487,6 +513,7 @@ macro_rules! param_test {
|
|||||||
($($name:ident: $string:expr, $value:expr)*) => {
|
($($name:ident: $string:expr, $value:expr)*) => {
|
||||||
$(
|
$(
|
||||||
mod $name {
|
mod $name {
|
||||||
|
#![allow(clippy::unwrap_used, clippy::unreadable_literal)]
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::{parse_stream, JsonStreamParser};
|
use super::{parse_stream, JsonStreamParser};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@@ -496,13 +523,13 @@ macro_rules! param_test {
|
|||||||
fn simple() {
|
fn simple() {
|
||||||
let string: &str = $string;
|
let string: &str = $string;
|
||||||
let value: Value = $value;
|
let value: Value = $value;
|
||||||
let result = parse_stream(&string);
|
let result = parse_stream(string);
|
||||||
assert_eq!(result.unwrap(), value);
|
assert_eq!(result.unwrap(), value);
|
||||||
let mut parser = JsonStreamParser::new();
|
let mut parser = JsonStreamParser::new();
|
||||||
for c in string.chars() {
|
for c in string.chars() {
|
||||||
parser.add_char(c).unwrap();
|
parser.add_char(c).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(parser.get_result(), &value);
|
assert_eq!(parser.result(), &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -517,7 +544,7 @@ macro_rules! param_test {
|
|||||||
for c in raw_json.chars() {
|
for c in raw_json.chars() {
|
||||||
parser.add_char(c).unwrap();
|
parser.add_char(c).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(parser.get_result(), &expected);
|
assert_eq!(parser.result(), &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -532,7 +559,7 @@ macro_rules! param_test {
|
|||||||
for c in raw_json.chars() {
|
for c in raw_json.chars() {
|
||||||
parser.add_char(c).unwrap();
|
parser.add_char(c).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(parser.get_result(), &expected);
|
assert_eq!(parser.result(), &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -547,7 +574,7 @@ macro_rules! param_test {
|
|||||||
for c in raw_json.chars() {
|
for c in raw_json.chars() {
|
||||||
parser.add_char(c).unwrap();
|
parser.add_char(c).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(parser.get_result(), &expected);
|
assert_eq!(parser.result(), &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -562,7 +589,7 @@ macro_rules! param_test {
|
|||||||
for c in raw_json.chars() {
|
for c in raw_json.chars() {
|
||||||
parser.add_char(c).unwrap();
|
parser.add_char(c).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(parser.get_result(), &expected);
|
assert_eq!(parser.result(), &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -580,7 +607,7 @@ macro_rules! param_test {
|
|||||||
for c in raw_json.chars() {
|
for c in raw_json.chars() {
|
||||||
parser.add_char(c).unwrap();
|
parser.add_char(c).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(parser.get_result(), &expected);
|
assert_eq!(parser.result(), &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -596,7 +623,7 @@ macro_rules! param_test {
|
|||||||
for c in raw_json.chars() {
|
for c in raw_json.chars() {
|
||||||
assert!(parser.add_char(c).is_ok(), "Add char error");
|
assert!(parser.add_char(c).is_ok(), "Add char error");
|
||||||
}
|
}
|
||||||
assert_eq!(parser.get_result(), &expected);
|
assert_eq!(parser.result(), &expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
@@ -607,18 +634,18 @@ param_test! {
|
|||||||
null: r#"null"#, Value::Null
|
null: r#"null"#, Value::Null
|
||||||
true_value: r#"true"#, Value::Bool(true)
|
true_value: r#"true"#, Value::Bool(true)
|
||||||
false_value: r#"false"#, Value::Bool(false)
|
false_value: r#"false"#, Value::Bool(false)
|
||||||
empty_string: r#""""#, Value::String("".to_string())
|
empty_string: r#""""#, Value::String(String::new())
|
||||||
single_character_string: r#""a""#, Value::String("a".to_string())
|
single_character_string: r#""a""#, Value::String("a".to_string())
|
||||||
string_with_spaces: r#""a b c""#, Value::String("a b c".to_string())
|
string_with_spaces: r#""a b c""#, Value::String("a b c".to_string())
|
||||||
string_with_space_at_end: r#""a b c ""#, Value::String("a b c ".to_string())
|
string_with_space_at_end: r#""a b c ""#, Value::String("a b c ".to_string())
|
||||||
string_with_space_at_start: r#"" a b c""#, Value::String(" a b c".to_string())
|
string_with_space_at_start: r#"" a b c""#, Value::String(" a b c".to_string())
|
||||||
string_with_space_at_start_and_end: r#"" a b c ""#, Value::String(" a b c ".to_string())
|
string_with_space_at_start_and_end: r#"" a b c ""#, Value::String(" a b c ".to_string())
|
||||||
number: r#"1234567890"#, Value::Number(1234567890.into())
|
number: r#"1234567890"#, Value::Number(1_234_567_890.into())
|
||||||
single_digit_number: r#"1"#, Value::Number(1.into())
|
single_digit_number: r#"1"#, Value::Number(1.into())
|
||||||
number_with_spaces_at_start: r#" 1234567890"#, Value::Number(1234567890.into())
|
number_with_spaces_at_start: r#" 1234567890"#, Value::Number(1_234_567_890.into())
|
||||||
number_with_spaces_at_end: r#"1234567890 "#, Value::Number(1234567890.into())
|
number_with_spaces_at_end: r#"1234567890 "#, Value::Number(1_234_567_890.into())
|
||||||
number_with_spaces_at_start_and_end: r#" 1234567890 "#, Value::Number(1234567890.into())
|
number_with_spaces_at_start_and_end: r#" 1234567890 "#, Value::Number(1_234_567_890.into())
|
||||||
negative_number: r#"-1234567890"#, Value::Number((-1234567890).into())
|
negative_number: r#"-1234567890"#, Value::Number((-1_234_567_890i64).into())
|
||||||
negative_single_digit_number: r#"-1"#, Value::Number((-1).into())
|
negative_single_digit_number: r#"-1"#, Value::Number((-1).into())
|
||||||
zero: r#"0"#, Value::Number(0.into())
|
zero: r#"0"#, Value::Number(0.into())
|
||||||
float: r#"123.456"#, Value::Number(serde_json::Number::from_f64(123.456).unwrap())
|
float: r#"123.456"#, Value::Number(serde_json::Number::from_f64(123.456).unwrap())
|
||||||
|
|||||||
Reference in New Issue
Block a user