use abi_stable::{
StableAbi,
std_types::{RHashMap, RString, RVec, Tuple2},
};
#[repr(u8)]
#[derive(Debug, Clone, StableAbi)]
pub enum ConfigValue {
String(RString),
Integer(i64),
Float(f64),
Bool(bool),
Array(RVec<ConfigValue>),
Table(RHashMap<RString, ConfigValue>),
#[doc(hidden)]
__Other(u8, RVec<u8>),
}
impl ConfigValue {
#[must_use]
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s.as_str()),
_ => None,
}
}
#[must_use]
pub fn as_integer(&self) -> Option<i64> {
match self {
Self::Integer(i) => Some(*i),
_ => None,
}
}
#[must_use]
pub fn as_float(&self) -> Option<f64> {
match self {
Self::Float(f) => Some(*f),
_ => None,
}
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Bool(b) => Some(*b),
_ => None,
}
}
#[must_use]
pub fn as_array(&self) -> Option<&RVec<ConfigValue>> {
match self {
Self::Array(a) => Some(a),
_ => None,
}
}
#[must_use]
pub fn as_table(&self) -> Option<&RHashMap<RString, ConfigValue>> {
match self {
Self::Table(t) => Some(t),
_ => None,
}
}
}
impl PartialEq for ConfigValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::String(a), Self::String(b)) => a == b,
(Self::Integer(a), Self::Integer(b)) => a == b,
(Self::Float(a), Self::Float(b)) => a == b,
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Array(a), Self::Array(b)) => a == b,
(Self::Table(a), Self::Table(b)) => {
a.len() == b.len()
&& a.iter()
.all(|Tuple2(k, v)| b.get(k).is_some_and(|bv| v == bv))
}
(Self::__Other(t1, d1), Self::__Other(t2, d2)) => t1 == t2 && d1 == d2,
_ => false,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, StableAbi)]
pub struct PluginContext {
pub name: RString,
pub config: RHashMap<RString, ConfigValue>,
}
#[repr(u8)]
#[derive(Debug, Clone, StableAbi)]
pub enum SpoeValue {
Null,
Bool(bool),
Int32(i32),
Uint32(u32),
Int64(i64),
Uint64(u64),
Ipv4([u8; 4]),
Ipv6([u8; 16]),
String(RString),
Binary(RVec<u8>),
#[doc(hidden)]
__Other(u8, RVec<u8>),
}
#[repr(C)]
#[derive(Debug, Clone, StableAbi)]
pub struct SpoeMessage {
pub name: RString,
pub args: RHashMap<RString, SpoeValue>,
pub stream_id: u64,
pub frame_id: u64,
}
#[repr(C)]
#[derive(Debug, Clone, StableAbi)]
pub struct ProcessingResult {
pub variables: RVec<TxnVariable>,
}
#[repr(C)]
#[derive(Debug, Clone, StableAbi)]
pub struct TxnVariable {
pub scope: VarScope,
pub name: RString,
pub value: SpoeValue,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, StableAbi)]
pub enum VarScope {
Process = 0,
Session = 1,
Transaction = 2,
Request = 3,
Response = 4,
#[doc(hidden)]
__Other(u8),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_value_as_str() {
let v = ConfigValue::String("hello".into());
assert_eq!(v.as_str(), Some("hello"));
assert_eq!(ConfigValue::Integer(1).as_str(), None);
}
#[test]
fn config_value_as_integer() {
let v = ConfigValue::Integer(42);
assert_eq!(v.as_integer(), Some(42));
assert_eq!(ConfigValue::String("x".into()).as_integer(), None);
}
#[test]
fn config_value_as_float() {
let v = ConfigValue::Float(2.72);
assert_eq!(v.as_float(), Some(2.72));
assert_eq!(ConfigValue::Integer(1).as_float(), None);
}
#[test]
fn config_value_as_bool() {
let v = ConfigValue::Bool(true);
assert_eq!(v.as_bool(), Some(true));
assert_eq!(ConfigValue::String("true".into()).as_bool(), None);
}
#[test]
fn config_value_as_array() {
let arr: RVec<ConfigValue> = vec![ConfigValue::Integer(1), ConfigValue::Integer(2)].into();
let v = ConfigValue::Array(arr.clone());
assert_eq!(v.as_array(), Some(&arr));
assert_eq!(ConfigValue::Integer(1).as_array(), None);
}
#[test]
fn config_value_as_table() {
let mut map = RHashMap::new();
map.insert(RString::from("key"), ConfigValue::Bool(true));
let v = ConfigValue::Table(map.clone());
assert!(v.as_table().is_some());
assert_eq!(ConfigValue::Integer(1).as_table(), None);
}
#[test]
fn config_value_partial_eq() {
assert_eq!(
ConfigValue::String("a".into()),
ConfigValue::String("a".into())
);
assert_ne!(
ConfigValue::String("a".into()),
ConfigValue::String("b".into())
);
assert_ne!(ConfigValue::String("1".into()), ConfigValue::Integer(1));
assert_eq!(ConfigValue::Integer(42), ConfigValue::Integer(42));
assert_eq!(ConfigValue::Float(1.0), ConfigValue::Float(1.0));
assert_eq!(ConfigValue::Bool(true), ConfigValue::Bool(true));
let arr1: RVec<ConfigValue> = vec![ConfigValue::Integer(1)].into();
let arr2: RVec<ConfigValue> = vec![ConfigValue::Integer(1)].into();
assert_eq!(ConfigValue::Array(arr1), ConfigValue::Array(arr2));
let mut t1 = RHashMap::new();
t1.insert(RString::from("k"), ConfigValue::Integer(1));
let mut t2 = RHashMap::new();
t2.insert(RString::from("k"), ConfigValue::Integer(1));
assert_eq!(ConfigValue::Table(t1), ConfigValue::Table(t2));
}
}