use std::io::Cursor;
#[cfg(feature = "serde")]
use {
serde::de::DeserializeOwned,
serde_json::Error as SerdeJsonError,
};
use crate::JSONParser;
use crate::utils::extract_json_to_string;
#[derive(Debug)]
#[cfg(feature = "serde")]
pub enum DeserializeError {
Extraction(String),
Deserialization(SerdeJsonError),
}
#[cfg(feature = "serde")]
impl std::fmt::Display for DeserializeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DeserializeError::Extraction(e) => write!(f, "JSON extraction error: {}", e),
DeserializeError::Deserialization(e) => write!(f, "JSON deserialization error: {}", e),
}
}
}
#[cfg(feature = "serde")]
impl std::error::Error for DeserializeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
DeserializeError::Extraction(_) => None,
DeserializeError::Deserialization(e) => Some(e),
}
}
}
#[cfg(feature = "serde")]
pub fn from_mixed_text<T>(input: &str) -> Result<T, DeserializeError>
where
T: DeserializeOwned,
{
let json = match extract_json_to_string(input) {
Ok(json) => json,
Err(e) => return Err(DeserializeError::Extraction(e.to_string())),
};
serde_json::from_str(&json).map_err(DeserializeError::Deserialization)
}
#[cfg(feature = "serde")]
pub fn from_mixed_text_with_parser<T>(
parser: &mut JSONParser,
input: &str,
) -> Result<T, DeserializeError>
where
T: DeserializeOwned,
{
let mut buffer = Vec::new();
{
let mut writer = Cursor::new(&mut buffer);
if let Err(e) = parser.extract_json_from_stream(&mut writer, input) {
return Err(DeserializeError::Extraction(e.to_string()));
}
}
let json = String::from_utf8(buffer)
.map_err(|e| DeserializeError::Extraction(e.to_string()))?;
if json.is_empty() && !parser.is_in_json() {
let mut buffer = Vec::new();
{
let mut writer = Cursor::new(&mut buffer);
if let Err(e) = parser.extract_json_from_stream(&mut writer, "") {
return Err(DeserializeError::Extraction(e.to_string()));
}
}
let complete_json = String::from_utf8(buffer)
.map_err(|e| DeserializeError::Extraction(e.to_string()))?;
if !complete_json.is_empty() {
return serde_json::from_str(&complete_json).map_err(DeserializeError::Deserialization);
}
}
if !parser.is_in_json() && !json.is_empty() {
serde_json::from_str(&json).map_err(DeserializeError::Deserialization)
} else {
Err(DeserializeError::Extraction(
"Incomplete JSON: parser is still expecting more input".to_string()
))
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Debug, PartialEq, Deserialize)]
struct TestStruct {
name: String,
value: i32,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestedStruct {
id: u64,
data: TestStruct,
}
#[test]
fn test_deserialize_simple_struct() {
let input = "Text before {\"name\":\"test\",\"value\":42} text after";
let result: TestStruct = from_mixed_text(input).unwrap();
assert_eq!(
result,
TestStruct {
name: "test".to_string(),
value: 42,
}
);
}
#[test]
fn test_deserialize_nested_struct() {
let input = "Data: {\"id\":123,\"data\":{\"name\":\"nested\",\"value\":99}}";
let result: NestedStruct = from_mixed_text(input).unwrap();
assert_eq!(
result,
NestedStruct {
id: 123,
data: TestStruct {
name: "nested".to_string(),
value: 99,
},
}
);
}
#[test]
fn test_deserialize_with_parser() {
let mut parser = JSONParser::new();
let json = "{\"name\":\"parser_test\",\"value\":42}";
let result: TestStruct = from_mixed_text_with_parser(&mut parser, json).unwrap();
assert_eq!(
result,
TestStruct {
name: "parser_test".to_string(),
value: 42,
}
);
}
#[test]
fn test_error_on_invalid_json() {
let input = "Invalid: {\"name\":\"test\",\"value\":\"not a number\"}";
let result: Result<TestStruct, _> = from_mixed_text(input);
assert!(result.is_err());
if let Err(DeserializeError::Deserialization(_)) = result {
} else {
panic!("Expected deserialization error");
}
}
#[test]
fn test_error_on_incomplete_json() {
let mut parser = JSONParser::new();
let input = "{\"name\":\"incomplete\"";
let result: Result<TestStruct, _> = from_mixed_text_with_parser(&mut parser, input);
assert!(result.is_err());
assert!(parser.is_in_json());
}
}