use serde_json::Value;
use std::sync::Arc;
pub enum ContextFrame {
Indexed { data: Value, index: usize },
Keyed {
data: Value,
index: usize,
key: String,
},
Reduce { current: Value, accumulator: Value },
Data(Value),
}
impl ContextFrame {
#[inline]
pub fn data(&self) -> &Value {
match self {
Self::Indexed { data, .. } | Self::Keyed { data, .. } | Self::Data(data) => data,
Self::Reduce { current, .. } => current,
}
}
#[inline]
pub fn get_index(&self) -> Option<usize> {
match self {
Self::Indexed { index, .. } | Self::Keyed { index, .. } => Some(*index),
_ => None,
}
}
#[inline]
pub fn get_key(&self) -> Option<&str> {
match self {
Self::Keyed { key, .. } => Some(key.as_str()),
_ => None,
}
}
#[inline]
pub fn get_reduce_current(&self) -> Option<&Value> {
match self {
Self::Reduce { current, .. } => Some(current),
_ => None,
}
}
#[inline]
pub fn get_reduce_accumulator(&self) -> Option<&Value> {
match self {
Self::Reduce { accumulator, .. } => Some(accumulator),
_ => None,
}
}
}
pub enum ContextFrameRef<'a> {
Frame(&'a ContextFrame),
Root(&'a Arc<Value>),
}
impl<'a> ContextFrameRef<'a> {
pub fn data(&self) -> &Value {
match self {
ContextFrameRef::Frame(frame) => frame.data(),
ContextFrameRef::Root(root) => root,
}
}
#[inline]
pub fn get_index(&self) -> Option<usize> {
match self {
ContextFrameRef::Frame(frame) => frame.get_index(),
ContextFrameRef::Root(_) => None,
}
}
#[inline]
pub fn get_key(&self) -> Option<&str> {
match self {
ContextFrameRef::Frame(frame) => frame.get_key(),
ContextFrameRef::Root(_) => None,
}
}
#[inline]
pub fn get_reduce_current(&self) -> Option<&Value> {
match self {
ContextFrameRef::Frame(frame) => frame.get_reduce_current(),
ContextFrameRef::Root(_) => None,
}
}
#[inline]
pub fn get_reduce_accumulator(&self) -> Option<&Value> {
match self {
ContextFrameRef::Frame(frame) => frame.get_reduce_accumulator(),
ContextFrameRef::Root(_) => None,
}
}
#[inline]
pub fn metadata(&self) -> Option<&std::collections::HashMap<String, Value>> {
None
}
}
pub struct ContextStack {
root: Arc<Value>,
frames: Vec<ContextFrame>,
}
impl ContextStack {
pub fn new(root: Arc<Value>) -> Self {
Self {
root,
frames: Vec::new(),
}
}
pub fn push(&mut self, data: Value) {
self.frames.push(ContextFrame::Data(data));
}
#[inline]
pub fn push_with_index(&mut self, data: Value, index: usize) {
self.frames.push(ContextFrame::Indexed { data, index });
}
#[inline]
pub fn push_with_key_index(&mut self, data: Value, index: usize, key: String) {
self.frames.push(ContextFrame::Keyed { data, index, key });
}
#[inline]
pub fn replace_top_key_data(&mut self, data: Value, index: usize, key: String) {
if let Some(frame) = self.frames.last_mut() {
*frame = ContextFrame::Keyed { data, index, key };
}
}
#[inline]
pub fn take_top_data(&mut self) -> Value {
if let Some(frame) = self.frames.last_mut() {
match frame {
ContextFrame::Indexed { data, .. }
| ContextFrame::Keyed { data, .. }
| ContextFrame::Data(data) => std::mem::replace(data, Value::Null),
ContextFrame::Reduce { current, .. } => std::mem::replace(current, Value::Null),
}
} else {
Value::Null
}
}
#[inline]
pub fn replace_top_data(&mut self, data: Value, index: usize) {
if let Some(frame) = self.frames.last_mut() {
*frame = ContextFrame::Indexed { data, index };
}
}
#[inline]
pub fn push_reduce(&mut self, current: Value, accumulator: Value) {
self.frames.push(ContextFrame::Reduce {
current,
accumulator,
});
}
#[inline]
pub fn replace_reduce_data(&mut self, current: Value, accumulator: Value) {
if let Some(frame) = self.frames.last_mut() {
*frame = ContextFrame::Reduce {
current,
accumulator,
};
}
}
pub fn pop(&mut self) -> Option<ContextFrame> {
self.frames.pop()
}
pub fn get_at_level(&self, level: isize) -> Option<ContextFrameRef<'_>> {
let levels_up = level.unsigned_abs();
if levels_up == 0 {
return Some(self.current());
}
let frame_count = self.frames.len();
if levels_up >= frame_count {
return Some(ContextFrameRef::Root(&self.root));
}
let target_index = frame_count - levels_up;
self.frames.get(target_index).map(ContextFrameRef::Frame)
}
pub fn current(&self) -> ContextFrameRef<'_> {
if let Some(frame) = self.frames.last() {
ContextFrameRef::Frame(frame)
} else {
ContextFrameRef::Root(&self.root)
}
}
pub fn root(&self) -> ContextFrameRef<'_> {
ContextFrameRef::Root(&self.root)
}
pub fn depth(&self) -> usize {
self.frames.len()
}
}