use crate::parser::scanner::{Range, ScanResult};
use crate::{Error, Result};
use smallvec::SmallVec;
#[derive(Debug, Clone)]
pub enum JsonValue<'a> {
Raw(&'a [u8]),
String(&'a str),
Number(&'a [u8]),
Bool(bool),
Null,
Array(LazyArray<'a>),
Object(LazyObject<'a>),
}
#[derive(Debug, Clone)]
pub struct LazyArray<'a> {
raw: &'a [u8],
boundaries: SmallVec<[Range; 32]>,
#[allow(dead_code)] cache: std::collections::HashMap<usize, JsonValue<'a>>,
}
#[derive(Debug, Clone)]
pub struct LazyObject<'a> {
raw: &'a [u8],
fields: SmallVec<[FieldRange; 16]>,
#[allow(dead_code)] cache: std::collections::HashMap<String, JsonValue<'a>>,
}
#[derive(Debug, Clone)]
pub struct FieldRange {
key: Range,
value: Range,
}
impl<'a> JsonValue<'a> {
pub fn as_str(&self) -> Option<&str> {
match self {
JsonValue::String(s) => Some(s),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
JsonValue::Number(bytes) => std::str::from_utf8(bytes).ok()?.parse().ok(),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
JsonValue::Number(bytes) => std::str::from_utf8(bytes).ok()?.parse().ok(),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
JsonValue::Bool(b) => Some(*b),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, JsonValue::Null)
}
pub fn as_array(&self) -> Option<&LazyArray<'a>> {
match self {
JsonValue::Array(arr) => Some(arr),
_ => None,
}
}
pub fn as_object(&self) -> Option<&LazyObject<'a>> {
match self {
JsonValue::Object(obj) => Some(obj),
_ => None,
}
}
pub fn parse_raw(&mut self) -> Result<()> {
match self {
JsonValue::Raw(_bytes) => {
*self = JsonValue::Null; Ok(())
}
_ => Ok(()),
}
}
}
impl<'a> LazyArray<'a> {
pub fn from_scan(raw: &'a [u8], scan_result: ScanResult) -> Self {
let boundaries = Self::extract_element_boundaries(raw, &scan_result);
Self {
raw,
boundaries,
cache: std::collections::HashMap::new(),
}
}
pub fn len(&self) -> usize {
self.boundaries.len()
}
pub fn is_empty(&self) -> bool {
self.boundaries.is_empty()
}
pub fn get(&self, index: usize) -> Option<&'a [u8]> {
if index >= self.boundaries.len() {
return None;
}
let range = self.boundaries[index];
Some(&self.raw[range.start..range.end])
}
pub fn get_parsed(&self, index: usize) -> Option<JsonValue<'a>> {
self.get(index).map(JsonValue::Raw)
}
pub fn iter(&'a self) -> LazyArrayIter<'a> {
LazyArrayIter {
array: self,
index: 0,
}
}
fn extract_element_boundaries(_raw: &[u8], _scan_result: &ScanResult) -> SmallVec<[Range; 32]> {
SmallVec::new()
}
pub fn is_numeric(&self) -> bool {
self.boundaries.len() > 4
&& self.boundaries.iter().take(3).all(|range| {
let slice = &self.raw[range.start..range.end];
self.looks_like_number(slice)
})
}
fn looks_like_number(&self, bytes: &[u8]) -> bool {
if bytes.is_empty() {
return false;
}
bytes.iter().all(|&b| {
b.is_ascii_digit() || b == b'.' || b == b'-' || b == b'+' || b == b'e' || b == b'E'
})
}
}
impl<'a> LazyObject<'a> {
pub fn from_scan(raw: &'a [u8], scan_result: ScanResult) -> Self {
let fields = Self::extract_field_boundaries(raw, &scan_result);
Self {
raw,
fields,
cache: std::collections::HashMap::new(),
}
}
pub fn len(&self) -> usize {
self.fields.len()
}
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
}
pub fn get(&self, key: &str) -> Option<&'a [u8]> {
let field_range = self.fields.iter().find(|field| {
let key_bytes = &self.raw[field.key.start..field.key.end];
std::str::from_utf8(key_bytes) == Ok(key)
})?;
Some(&self.raw[field_range.value.start..field_range.value.end])
}
pub fn keys(&self) -> Result<Vec<&str>> {
self.fields
.iter()
.map(|field| {
let key_bytes = &self.raw[field.key.start..field.key.end];
std::str::from_utf8(key_bytes).map_err(Error::from)
})
.collect()
}
fn extract_field_boundaries(
_raw: &[u8],
_scan_result: &ScanResult,
) -> SmallVec<[FieldRange; 16]> {
SmallVec::new()
}
}
pub struct LazyArrayIter<'a> {
array: &'a LazyArray<'a>,
index: usize,
}
impl<'a> Iterator for LazyArrayIter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.array.boundaries.len() {
return None;
}
let range = self.array.boundaries[self.index];
self.index += 1;
Some(&self.array.raw[range.start..range.end])
}
}
impl FieldRange {
pub fn new(key: Range, value: Range) -> Self {
Self { key, value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_json_value_types() {
let val = JsonValue::String("hello");
assert_eq!(val.as_str(), Some("hello"));
assert!(val.as_f64().is_none());
}
#[test]
fn test_lazy_array_creation() {
let raw = b"[1, 2, 3]";
let scan_result = ScanResult::new();
let array = LazyArray::from_scan(raw, scan_result);
assert_eq!(array.len(), 0); }
#[test]
fn test_number_detection() {
let raw = b"[1.0, 2.5, 3.14]";
let scan_result = ScanResult::new();
let array = LazyArray::from_scan(raw, scan_result);
assert!(array.looks_like_number(b"123.45"));
assert!(!array.looks_like_number(b"\"string\""));
}
}