use crate::NodeId;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
Node(NodeId),
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
DateTime(String),
Typed { value: String, datatype: String },
LangString { value: String, lang: String },
Bytes(Vec<u8>),
Json(serde_json::Value),
Null,
}
impl Value {
pub fn literal(s: impl Into<String>) -> Self {
Self::String(s.into())
}
pub fn node(node: NodeId) -> Self {
Self::Node(node)
}
pub fn integer(n: i64) -> Self {
Self::Integer(n)
}
pub fn float(f: f64) -> Self {
Self::Float(f)
}
pub fn boolean(b: bool) -> Self {
Self::Boolean(b)
}
pub fn datetime(dt: impl Into<String>) -> Self {
Self::DateTime(dt.into())
}
pub fn typed(value: impl Into<String>, datatype: impl Into<String>) -> Self {
Self::Typed {
value: value.into(),
datatype: datatype.into(),
}
}
pub fn lang_string(value: impl Into<String>, lang: impl Into<String>) -> Self {
Self::LangString {
value: value.into(),
lang: lang.into(),
}
}
pub fn bytes(data: Vec<u8>) -> Self {
Self::Bytes(data)
}
pub fn json(value: serde_json::Value) -> Self {
Self::Json(value)
}
pub fn null() -> Self {
Self::Null
}
pub fn is_node(&self) -> bool {
matches!(self, Self::Node(_))
}
pub fn is_literal(&self) -> bool {
!matches!(self, Self::Node(_) | Self::Null)
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn as_node(&self) -> Option<&NodeId> {
match self {
Self::Node(n) => Some(n),
_ => None,
}
}
pub fn as_string(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s),
Self::LangString { value, .. } => Some(value),
Self::Typed { value, .. } => Some(value),
_ => None,
}
}
pub fn as_integer(&self) -> Option<i64> {
match self {
Self::Integer(n) => Some(*n),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
Value::Float(f) => Some(*f),
Value::Integer(n) => Some(*n as f64),
_ => None,
}
}
pub fn as_boolean(&self) -> Option<bool> {
match self {
Self::Boolean(b) => Some(*b),
_ => None,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
bincode::serde::encode_to_vec(self, bincode::config::standard()).unwrap_or_default()
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
bincode::serde::decode_from_slice(bytes, bincode::config::standard())
.map(|(v, _)| v)
.ok()
}
pub fn sort_key(&self) -> Vec<u8> {
match self {
Self::Node(n) => {
let mut key = vec![0u8]; key.extend(n.to_bytes());
key
}
Self::String(s) => {
let mut key = vec![1u8];
key.extend(s.as_bytes());
key
}
Self::Integer(n) => {
let mut key = vec![2u8];
key.extend(&((*n as u64) ^ (1u64 << 63)).to_be_bytes());
key
}
Self::Float(f) => {
let mut key = vec![3u8];
let bits = f.to_bits();
let sortable = if *f >= 0.0 {
bits ^ (1u64 << 63)
} else {
!bits
};
key.extend(&sortable.to_be_bytes());
key
}
Self::Boolean(b) => {
vec![4u8, if *b { 1 } else { 0 }]
}
Self::DateTime(dt) => {
let mut key = vec![5u8];
key.extend(dt.as_bytes());
key
}
Self::Typed { value, datatype } => {
let mut key = vec![6u8];
key.extend(datatype.as_bytes());
key.push(0); key.extend(value.as_bytes());
key
}
Self::LangString { value, lang } => {
let mut key = vec![7u8];
key.extend(lang.as_bytes());
key.push(0);
key.extend(value.as_bytes());
key
}
Self::Bytes(data) => {
let mut key = vec![8u8];
key.extend(data);
key
}
Self::Json(v) => {
let mut key = vec![9u8];
if let Ok(s) = serde_json::to_string(v) {
key.extend(s.as_bytes());
}
key
}
Self::Null => vec![255u8], }
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Node(n) => write!(f, "{}", n),
Self::String(s) => write!(f, "\"{}\"", s),
Self::Integer(n) => write!(f, "{}", n),
Self::Float(n) => write!(f, "{}", n),
Self::Boolean(b) => write!(f, "{}", b),
Self::DateTime(dt) => write!(f, "\"{}\"^^xsd:dateTime", dt),
Self::Typed { value, datatype } => write!(f, "\"{}\"^^<{}>", value, datatype),
Self::LangString { value, lang } => write!(f, "\"{}\"@{}", value, lang),
Self::Bytes(data) => write!(f, "_:bytes[{}]", data.len()),
Self::Json(v) => write!(f, "{}", v),
Self::Null => write!(f, "null"),
}
}
}
impl Eq for Value {}
impl std::hash::Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.sort_key().hash(state);
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Value {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.sort_key().cmp(&other.sort_key())
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Self::String(s.to_string())
}
}
impl From<i64> for Value {
fn from(n: i64) -> Self {
Self::Integer(n)
}
}
impl From<i32> for Value {
fn from(n: i32) -> Self {
Self::Integer(n as i64)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Self::Float(f)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Self::Boolean(b)
}
}
impl From<NodeId> for Value {
fn from(n: NodeId) -> Self {
Self::Node(n)
}
}
impl From<serde_json::Value> for Value {
fn from(v: serde_json::Value) -> Self {
Self::Json(v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_literal_value() {
let val = Value::literal("hello");
assert!(val.is_literal());
assert_eq!(val.as_string(), Some("hello"));
}
#[test]
fn test_node_value() {
let node = NodeId::named("user:alice");
let val = Value::node(node.clone());
assert!(val.is_node());
assert_eq!(val.as_node(), Some(&node));
}
#[test]
fn test_numeric_values() {
let int_val = Value::integer(42);
assert_eq!(int_val.as_integer(), Some(42));
let float_val = Value::float(3.14);
assert_eq!(float_val.as_float(), Some(3.14));
}
#[test]
fn test_display() {
assert_eq!(format!("{}", Value::literal("test")), "\"test\"");
assert_eq!(format!("{}", Value::integer(42)), "42");
assert_eq!(format!("{}", Value::boolean(true)), "true");
}
#[test]
fn test_serialization() {
let val = Value::literal("test");
let bytes = val.to_bytes();
let restored = Value::from_bytes(&bytes).unwrap();
assert_eq!(val, restored);
}
#[test]
fn test_sort_order() {
let v1 = Value::integer(1);
let v2 = Value::integer(2);
let v3 = Value::integer(-1);
assert!(v3 < v1);
assert!(v1 < v2);
}
#[test]
fn test_conversions() {
let s: Value = "hello".into();
assert_eq!(s.as_string(), Some("hello"));
let n: Value = 42i64.into();
assert_eq!(n.as_integer(), Some(42));
let b: Value = true.into();
assert_eq!(b.as_boolean(), Some(true));
}
}