use crate::schema::FilterSchema;
use crate::types::FieldType;
use crate::types::LiteralValue;
use crate::WirerustError;
use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FilterContext {
field_values: Vec<Option<LiteralValue>>, }
pub struct FilterContextBuilder<'a> {
ctx: FilterContext,
schema: &'a FilterSchema,
}
impl<'a> FilterContextBuilder<'a> {
pub fn new(schema: &'a FilterSchema) -> Self {
Self {
ctx: FilterContext::new(),
schema,
}
}
pub fn build(self) -> FilterContext {
self.ctx
}
pub fn set_int(mut self, field: &str, value: i64) -> Result<Self, WirerustError> {
self.ctx.set_int(field, value, self.schema);
Ok(self)
}
pub fn set_bool(mut self, field: &str, value: bool) -> Result<Self, WirerustError> {
self.ctx.set_bool(field, value, self.schema);
Ok(self)
}
pub fn set_ip(mut self, field: &str, value: IpAddr) -> Result<Self, WirerustError> {
self.ctx.set_ip(field, value, self.schema);
Ok(self)
}
pub fn set_bytes(
mut self,
field: &str,
value: impl AsRef<[u8]>,
) -> Result<Self, WirerustError> {
self.ctx.set_bytes(field, value, self.schema);
Ok(self)
}
pub fn set_array(
mut self,
field: &str,
value: Vec<LiteralValue>,
) -> Result<Self, WirerustError> {
self.ctx.set_array(field, value, self.schema);
Ok(self)
}
}
impl FilterContext {
pub fn new() -> Self {
Self::default()
}
pub fn set_by_id(&mut self, field_id: usize, value: LiteralValue) {
if self.field_values.len() <= field_id {
self.field_values.resize(field_id + 1, None);
}
self.field_values[field_id] = Some(value);
}
pub fn get_by_id(&self, field_id: usize) -> Option<&LiteralValue> {
self.field_values.get(field_id).and_then(|v| v.as_ref())
}
pub fn set(
&mut self,
field: &str,
value: LiteralValue,
schema: &FilterSchema,
) -> Result<(), WirerustError> {
match schema.get_field_type(field) {
Some(expected_type) => {
let value_type = value.get_type();
if let (FieldType::Array(_expected_elem), FieldType::Array(value_elem)) =
(expected_type, &value_type)
{
if let FieldType::Unknown = **value_elem {
if let Some(fid) = schema.field_id(field) {
self.set_by_id(fid, value.clone());
}
return Ok(());
}
}
if &value_type == expected_type {
if let Some(fid) = schema.field_id(field) {
self.set_by_id(fid, value.clone());
}
Ok(())
} else {
Err(WirerustError::TypeError(format!("Type mismatch for field '{field}': expected {expected_type:?}, got {value_type:?}")))
}
}
None => Err(WirerustError::FieldNotFound(field.to_string())),
}
}
pub fn get(&self, field: &str, schema: &FilterSchema) -> Option<&LiteralValue> {
schema.field_id(field).and_then(|fid| self.get_by_id(fid))
}
pub fn set_int(&mut self, field: &str, value: i64, schema: &FilterSchema) -> &mut Self {
let _ = self.set(field, LiteralValue::Int(value), schema);
self
}
pub fn set_bool(&mut self, field: &str, value: bool, schema: &FilterSchema) -> &mut Self {
let _ = self.set(field, LiteralValue::Bool(value), schema);
self
}
pub fn set_ip(&mut self, field: &str, value: IpAddr, schema: &FilterSchema) -> &mut Self {
let _ = self.set(field, LiteralValue::Ip(value), schema);
self
}
pub fn set_bytes<T: AsRef<[u8]>>(
&mut self,
field: &str,
value: T,
schema: &FilterSchema,
) -> &mut Self {
let _ = self.set(
field,
LiteralValue::Bytes(Arc::new(value.as_ref().to_vec())),
schema,
);
self
}
pub fn set_array(
&mut self,
field: &str,
value: Vec<LiteralValue>,
schema: &FilterSchema,
) -> &mut Self {
let _ = self.set(field, LiteralValue::Array(Arc::new(value)), schema);
self
}
pub fn get_int(&self, field: &str, schema: &FilterSchema) -> Option<i64> {
match self.get(field, schema) {
Some(LiteralValue::Int(i)) => Some(*i),
_ => None,
}
}
pub fn get_bool(&self, field: &str, schema: &FilterSchema) -> Option<bool> {
match self.get(field, schema) {
Some(LiteralValue::Bool(b)) => Some(*b),
_ => None,
}
}
pub fn get_ip(&self, field: &str, schema: &FilterSchema) -> Option<IpAddr> {
match self.get(field, schema) {
Some(LiteralValue::Ip(ip)) => Some(*ip),
_ => None,
}
}
pub fn get_bytes(&self, field: &str, schema: &FilterSchema) -> Option<&[u8]> {
match self.get(field, schema) {
Some(LiteralValue::Bytes(b)) => Some(&b[..]),
_ => None,
}
}
pub fn get_array(&self, field: &str, schema: &FilterSchema) -> Option<Arc<Vec<LiteralValue>>> {
match self.get(field, schema) {
Some(LiteralValue::Array(arr)) => Some(Arc::clone(arr)),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::schema::FilterSchemaBuilder;
use crate::types::{FieldType, LiteralValue};
use std::net::IpAddr;
use std::str::FromStr;
fn schema() -> FilterSchema {
FilterSchemaBuilder::new()
.field("foo", FieldType::Int)
.field("bar", FieldType::Bytes)
.field("arr", FieldType::Array(Box::new(FieldType::Int)))
.field("flag", FieldType::Bool)
.field("ip", FieldType::Ip)
.build()
}
#[test]
fn test_context_builder_and_typed_setters() {
let sch = schema();
let ip = IpAddr::from_str("127.0.0.1").unwrap();
let ctx = FilterContextBuilder::new(&sch)
.set_int("foo", 42)
.unwrap()
.set_bool("flag", true)
.unwrap()
.set_ip("ip", ip)
.unwrap()
.set_array("arr", vec![LiteralValue::Int(1), LiteralValue::Int(2)])
.unwrap()
.build();
assert_eq!(ctx.get_int("foo", &sch), Some(42));
assert_eq!(ctx.get_bool("flag", &sch), Some(true));
assert_eq!(ctx.get_ip("ip", &sch), Some(ip));
assert_eq!(
ctx.get_array("arr", &sch),
Some(Arc::new(vec![LiteralValue::Int(1), LiteralValue::Int(2)]))
);
}
#[test]
fn test_typed_setters_and_getters() {
let sch = schema();
let mut ctx = FilterContext::new();
let ip = IpAddr::from_str("192.168.1.1").unwrap();
ctx.set_int("foo", 123, &sch)
.set_bool("flag", false, &sch)
.set_ip("ip", ip, &sch);
assert_eq!(ctx.get_int("foo", &sch), Some(123));
assert_eq!(ctx.get_bool("flag", &sch), Some(false));
assert_eq!(ctx.get_ip("ip", &sch), Some(ip));
}
#[test]
fn test_set_and_get_value() {
let mut ctx = FilterContext::new();
let sch = schema();
ctx.set("foo", LiteralValue::Int(42), &sch).unwrap();
assert_eq!(ctx.get("foo", &sch), Some(&LiteralValue::Int(42)));
}
#[test]
fn test_type_checking() {
let mut ctx = FilterContext::new();
let sch = schema();
let res = ctx.set(
"foo",
LiteralValue::Bytes(Arc::new(b"not an int".to_vec())),
&sch,
);
assert!(res.is_err());
assert!(ctx.set("foo", LiteralValue::Int(1), &sch).is_ok());
}
#[test]
fn test_field_not_found() {
let mut ctx = FilterContext::new();
let sch = schema();
let res = ctx.set("unknown", LiteralValue::Int(1), &sch);
assert!(matches!(res, Err(WirerustError::FieldNotFound(_))));
}
#[test]
fn test_array_type_checking() {
let mut ctx = FilterContext::new();
let sch = schema();
let arr = LiteralValue::Array(Arc::new(vec![LiteralValue::Int(1), LiteralValue::Int(2)]));
assert!(ctx.set("arr", arr, &sch).is_ok());
let arr = LiteralValue::Array(Arc::new(vec![LiteralValue::Bytes(Arc::new(
b"bad".to_vec(),
))]));
let res = ctx.set("arr", arr, &sch);
assert!(res.is_ok() || res.is_err());
}
#[test]
fn test_serialization_deserialization() {
let mut ctx = FilterContext::new();
let sch = schema();
ctx.set("foo", LiteralValue::Int(123), &sch).unwrap();
ctx.set("bar", LiteralValue::Bytes(Arc::new(b"abc".to_vec())), &sch)
.unwrap();
let json = serde_json::to_string(&ctx).unwrap();
let deserialized: FilterContext = serde_json::from_str(&json).unwrap();
assert_eq!(ctx.get("foo", &sch), deserialized.get("foo", &sch));
assert_eq!(ctx.get("bar", &sch), deserialized.get("bar", &sch));
}
}