use crate::QuaminaError;
use crate::flatten_json::{self, ArrayPos};
use crate::segments_tree::SegmentsTree;
use std::any::Any;
#[derive(Clone, Debug)]
pub struct OwnedField {
pub path: Vec<u8>,
pub val: Vec<u8>,
pub array_trail: Vec<ArrayPos>,
pub is_number: bool,
}
pub trait SegmentsTreeTracker: Send + Sync {
fn get(&self, segment: &[u8]) -> Option<&dyn SegmentsTreeTracker>;
fn is_root(&self) -> bool;
fn is_segment_used(&self, segment: &[u8]) -> bool;
fn path_for_segment(&self, segment: &[u8]) -> Option<&[u8]>;
fn nodes_count(&self) -> usize;
fn fields_count(&self) -> usize;
fn as_any(&self) -> &dyn Any;
}
pub trait Flattener: Send + Sync {
fn flatten(
&mut self,
event: &[u8],
tracker: &dyn SegmentsTreeTracker,
) -> Result<Vec<OwnedField>, QuaminaError>;
fn copy(&self) -> Box<dyn Flattener>;
}
impl SegmentsTreeTracker for SegmentsTree {
fn get(&self, segment: &[u8]) -> Option<&dyn SegmentsTreeTracker> {
Self::get(self, segment).map(|t| t as &dyn SegmentsTreeTracker)
}
fn is_root(&self) -> bool {
Self::is_root(self)
}
fn is_segment_used(&self, segment: &[u8]) -> bool {
Self::is_segment_used(self, segment)
}
fn path_for_segment(&self, segment: &[u8]) -> Option<&[u8]> {
Self::path_for_segment(self, segment)
}
fn nodes_count(&self) -> usize {
Self::nodes_count(self)
}
fn fields_count(&self) -> usize {
Self::fields_count(self)
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Default)]
pub struct JsonFlattener {
state: flatten_json::State,
}
impl JsonFlattener {
#[must_use]
pub fn new() -> Self {
Self {
state: flatten_json::State::new(),
}
}
}
impl Flattener for JsonFlattener {
fn flatten(
&mut self,
event: &[u8],
tracker: &dyn SegmentsTreeTracker,
) -> Result<Vec<OwnedField>, QuaminaError> {
let segments_tree = tracker
.as_any()
.downcast_ref::<SegmentsTree>()
.ok_or_else(|| {
QuaminaError::InvalidJson("JsonFlattener requires SegmentsTree as tracker".into())
})?;
let fields = self.state.flatten(event, segments_tree)?;
Ok(fields
.iter()
.map(|f| OwnedField {
path: f.path.to_vec(),
val: f.val.as_bytes().to_vec(),
array_trail: f.array_trail.to_vec(),
is_number: f.is_number,
})
.collect())
}
fn copy(&self) -> Box<dyn Flattener> {
Box::new(Self::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_segments_tree_tracker_impl() {
let mut tree = SegmentsTree::new();
tree.add("status");
tree.add("context\nuser\nid");
let tracker: &dyn SegmentsTreeTracker = &tree;
assert!(tracker.is_root());
assert!(tracker.is_segment_used(b"status"));
assert!(tracker.is_segment_used(b"context"));
assert!(!tracker.is_segment_used(b"missing"));
let context = tracker.get(b"context").unwrap();
assert!(!context.is_root());
assert!(context.is_segment_used(b"user"));
let user = context.get(b"user").unwrap();
assert!(user.is_segment_used(b"id"));
assert_eq!(
user.path_for_segment(b"id"),
Some(b"context\nuser\nid".as_slice())
);
assert_eq!(tracker.nodes_count(), 1); assert_eq!(tracker.fields_count(), 1); assert_eq!(context.nodes_count(), 1); assert_eq!(context.fields_count(), 0);
assert_eq!(user.nodes_count(), 0);
assert_eq!(user.fields_count(), 1); }
#[test]
fn test_owned_field() {
let field = OwnedField {
path: b"status".to_vec(),
val: b"\"active\"".to_vec(),
array_trail: vec![],
is_number: false,
};
assert_eq!(field.path, b"status");
assert_eq!(field.val, b"\"active\"");
assert!(!field.is_number);
}
}