use serde::de::DeserializeOwned;
use serde_json::{Value, error::Category};
use crate::{JsonDecodeError, JsonDecodeOptions, JsonTopLevelKind, LenientJsonNormalizer};
#[derive(Debug, Clone, Default)]
pub struct LenientJsonDecoder {
normalizer: LenientJsonNormalizer,
}
impl LenientJsonDecoder {
#[must_use]
pub const fn new(options: JsonDecodeOptions) -> Self {
Self {
normalizer: LenientJsonNormalizer::new(options),
}
}
#[must_use]
pub const fn options(&self) -> &JsonDecodeOptions {
self.normalizer.options()
}
pub fn decode<T>(&self, input: &str) -> Result<T, JsonDecodeError>
where
T: DeserializeOwned,
{
let normalized = self.normalizer.normalize(input)?;
serde_json::from_str(normalized.as_ref())
.map_err(|error| Self::map_decode_error(error, normalized.len()))
}
pub fn decode_object<T>(&self, input: &str) -> Result<T, JsonDecodeError>
where
T: DeserializeOwned,
{
let normalized = self.normalizer.normalize(input)?;
self.ensure_top_level_from_text(normalized.as_ref(), JsonTopLevelKind::Object)?;
serde_json::from_str(normalized.as_ref())
.map_err(|error| Self::map_decode_error(error, normalized.len()))
}
pub fn decode_array<T>(&self, input: &str) -> Result<Vec<T>, JsonDecodeError>
where
T: DeserializeOwned,
{
let normalized = self.normalizer.normalize(input)?;
self.ensure_top_level_from_text(normalized.as_ref(), JsonTopLevelKind::Array)?;
serde_json::from_str(normalized.as_ref())
.map_err(|error| Self::map_decode_error(error, normalized.len()))
}
pub fn decode_value(&self, input: &str) -> Result<Value, JsonDecodeError> {
let normalized = self.normalizer.normalize(input)?;
serde_json::from_str(normalized.as_ref())
.map_err(|error| JsonDecodeError::invalid_json(error, Some(normalized.len())))
}
fn ensure_top_level_from_text(
&self,
normalized: &str,
expected: JsonTopLevelKind,
) -> Result<(), JsonDecodeError> {
if let Some(actual) = Self::classify_top_level_from_text(normalized)
&& actual != expected
{
return Err(JsonDecodeError::unexpected_top_level(expected, actual));
}
Ok(())
}
fn classify_top_level_from_text(input: &str) -> Option<JsonTopLevelKind> {
let first = input.chars().find(|ch| !ch.is_whitespace())?;
match first {
'{' => Some(JsonTopLevelKind::Object),
'[' => Some(JsonTopLevelKind::Array),
'"' | '-' | '0'..='9' | 't' | 'f' | 'n' => Some(JsonTopLevelKind::Other),
_ => None,
}
}
fn map_decode_error(error: serde_json::Error, input_bytes: usize) -> JsonDecodeError {
match error.classify() {
Category::Data => JsonDecodeError::deserialize(error, Some(input_bytes)),
Category::Io | Category::Syntax | Category::Eof => {
JsonDecodeError::invalid_json(error, Some(input_bytes))
}
}
}
}