use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum LogicalValue {
Null,
Bool(bool),
Int(i64),
Float(f64),
String(String),
Bytes(Vec<u8>),
Timestamp(DateTime<Utc>),
Json(serde_json::Value),
Array(Vec<LogicalValue>),
}
impl LogicalValue {
pub fn type_token(&self) -> &'static str {
match self {
Self::Null => "null",
Self::Bool(_) => "bool",
Self::Int(_) => "int",
Self::Float(_) => "float",
Self::String(_) => "string",
Self::Bytes(_) => "bytes",
Self::Timestamp(_) => "timestamp",
Self::Json(_) => "json",
Self::Array(_) => "array",
}
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn value_round_trips_through_serde() {
let cases = [
LogicalValue::Null,
LogicalValue::Bool(true),
LogicalValue::Int(42),
LogicalValue::Float(2.5),
LogicalValue::String("hello".to_string()),
LogicalValue::Bytes(vec![1, 2, 3]),
LogicalValue::Json(serde_json::json!({"k": "v"})),
LogicalValue::Array(vec![
LogicalValue::Int(1),
LogicalValue::Int(2),
LogicalValue::Null,
]),
];
for case in &cases {
let s = serde_json::to_string(case).expect("serialize");
let back: LogicalValue = serde_json::from_str(&s).expect("deserialize");
assert_eq!(case, &back, "round-trip mismatch for {}", case.type_token());
}
}
#[test]
fn type_tokens_are_pinned() {
assert_eq!(LogicalValue::Null.type_token(), "null");
assert_eq!(LogicalValue::Bool(false).type_token(), "bool");
assert_eq!(LogicalValue::Int(0).type_token(), "int");
assert_eq!(LogicalValue::Float(0.0).type_token(), "float");
assert_eq!(LogicalValue::String(String::new()).type_token(), "string");
}
}