use crate::seqstring::global_string;
use crate::value::{MapKey as RuntimeMapKey, Value, VariantData};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
#[derive(Debug)]
pub enum SerializeError {
QuotationNotSerializable,
ClosureNotSerializable,
ChannelNotSerializable,
BincodeError(Box<bincode::Error>),
InvalidData(String),
NonFiniteFloat(f64),
}
impl std::fmt::Display for SerializeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SerializeError::QuotationNotSerializable => {
write!(f, "Quotations cannot be serialized - code is not data")
}
SerializeError::ClosureNotSerializable => {
write!(f, "Closures cannot be serialized - code is not data")
}
SerializeError::ChannelNotSerializable => {
write!(f, "Channels cannot be serialized - runtime state")
}
SerializeError::BincodeError(e) => write!(f, "Bincode error: {}", e),
SerializeError::InvalidData(msg) => write!(f, "Invalid data: {}", msg),
SerializeError::NonFiniteFloat(v) => {
write!(f, "Cannot serialize non-finite float: {}", v)
}
}
}
}
impl std::error::Error for SerializeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SerializeError::BincodeError(e) => Some(e.as_ref()),
_ => None,
}
}
}
impl From<bincode::Error> for SerializeError {
fn from(e: bincode::Error) -> Self {
SerializeError::BincodeError(Box::new(e))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TypedMapKey {
Int(i64),
Bool(bool),
String(String),
}
impl TypedMapKey {
pub fn to_typed_value(&self) -> TypedValue {
match self {
TypedMapKey::Int(v) => TypedValue::Int(*v),
TypedMapKey::Bool(v) => TypedValue::Bool(*v),
TypedMapKey::String(v) => TypedValue::String(v.clone()),
}
}
pub fn from_runtime(key: &RuntimeMapKey) -> Self {
match key {
RuntimeMapKey::Int(v) => TypedMapKey::Int(*v),
RuntimeMapKey::Bool(v) => TypedMapKey::Bool(*v),
RuntimeMapKey::String(s) => TypedMapKey::String(s.as_str().to_string()),
}
}
pub fn to_runtime(&self) -> RuntimeMapKey {
match self {
TypedMapKey::Int(v) => RuntimeMapKey::Int(*v),
TypedMapKey::Bool(v) => RuntimeMapKey::Bool(*v),
TypedMapKey::String(s) => RuntimeMapKey::String(global_string(s.clone())),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TypedValue {
Int(i64),
Float(f64),
Bool(bool),
String(String),
Symbol(String),
Map(BTreeMap<TypedMapKey, TypedValue>),
Variant {
tag: String,
fields: Vec<TypedValue>,
},
}
impl TypedValue {
pub fn from_value(value: &Value) -> Result<Self, SerializeError> {
match value {
Value::Int(v) => Ok(TypedValue::Int(*v)),
Value::Float(v) => {
if !v.is_finite() {
return Err(SerializeError::NonFiniteFloat(*v));
}
Ok(TypedValue::Float(*v))
}
Value::Bool(v) => Ok(TypedValue::Bool(*v)),
Value::String(s) => Ok(TypedValue::String(s.as_str().to_string())),
Value::Symbol(s) => Ok(TypedValue::Symbol(s.as_str().to_string())),
Value::Map(map) => {
let mut typed_map = BTreeMap::new();
for (k, v) in map.iter() {
let typed_key = TypedMapKey::from_runtime(k);
let typed_value = TypedValue::from_value(v)?;
typed_map.insert(typed_key, typed_value);
}
Ok(TypedValue::Map(typed_map))
}
Value::Variant(data) => {
let mut typed_fields = Vec::with_capacity(data.fields.len());
for field in data.fields.iter() {
typed_fields.push(TypedValue::from_value(field)?);
}
Ok(TypedValue::Variant {
tag: data.tag.as_str().to_string(),
fields: typed_fields,
})
}
Value::Quotation { .. } => Err(SerializeError::QuotationNotSerializable),
Value::Closure { .. } => Err(SerializeError::ClosureNotSerializable),
Value::Channel(_) => Err(SerializeError::ChannelNotSerializable),
Value::WeaveCtx { .. } => Err(SerializeError::ChannelNotSerializable), }
}
pub fn to_value(&self) -> Value {
match self {
TypedValue::Int(v) => Value::Int(*v),
TypedValue::Float(v) => Value::Float(*v),
TypedValue::Bool(v) => Value::Bool(*v),
TypedValue::String(s) => Value::String(global_string(s.clone())),
TypedValue::Symbol(s) => Value::Symbol(global_string(s.clone())),
TypedValue::Map(map) => {
let mut runtime_map = HashMap::new();
for (k, v) in map.iter() {
runtime_map.insert(k.to_runtime(), v.to_value());
}
Value::Map(Box::new(runtime_map))
}
TypedValue::Variant { tag, fields } => {
let runtime_fields: Vec<Value> = fields.iter().map(|f| f.to_value()).collect();
Value::Variant(Arc::new(VariantData::new(
global_string(tag.clone()),
runtime_fields,
)))
}
}
}
pub fn to_map_key(&self) -> Result<TypedMapKey, SerializeError> {
match self {
TypedValue::Int(v) => Ok(TypedMapKey::Int(*v)),
TypedValue::Bool(v) => Ok(TypedMapKey::Bool(*v)),
TypedValue::String(v) => Ok(TypedMapKey::String(v.clone())),
TypedValue::Float(_) => Err(SerializeError::InvalidData(
"Float cannot be a map key".to_string(),
)),
TypedValue::Map(_) => Err(SerializeError::InvalidData(
"Map cannot be a map key".to_string(),
)),
TypedValue::Variant { .. } => Err(SerializeError::InvalidData(
"Variant cannot be a map key".to_string(),
)),
TypedValue::Symbol(v) => Ok(TypedMapKey::String(v.clone())),
}
}
pub fn to_bytes(&self) -> Result<Vec<u8>, SerializeError> {
bincode::serialize(self).map_err(SerializeError::from)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, SerializeError> {
bincode::deserialize(bytes).map_err(SerializeError::from)
}
pub fn to_debug_string(&self) -> String {
match self {
TypedValue::Int(v) => format!("{}", v),
TypedValue::Float(v) => format!("{}", v),
TypedValue::Bool(v) => format!("{}", v),
TypedValue::String(v) => format!("{:?}", v),
TypedValue::Symbol(v) => format!(":{}", v),
TypedValue::Map(m) => {
let entries: Vec<String> = m
.iter()
.map(|(k, v)| format!("{}: {}", key_to_debug_string(k), v.to_debug_string()))
.collect();
format!("{{ {} }}", entries.join(", "))
}
TypedValue::Variant { tag, fields } => {
if fields.is_empty() {
format!("(Variant#{})", tag)
} else {
let field_strs: Vec<String> =
fields.iter().map(|f| f.to_debug_string()).collect();
format!("(Variant#{} {})", tag, field_strs.join(" "))
}
}
}
}
}
fn key_to_debug_string(key: &TypedMapKey) -> String {
match key {
TypedMapKey::Int(v) => format!("{}", v),
TypedMapKey::Bool(v) => format!("{}", v),
TypedMapKey::String(v) => format!("{:?}", v),
}
}
pub trait ValueSerialize {
fn to_typed(&self) -> Result<TypedValue, SerializeError>;
fn to_bytes(&self) -> Result<Vec<u8>, SerializeError>;
}
impl ValueSerialize for Value {
fn to_typed(&self) -> Result<TypedValue, SerializeError> {
TypedValue::from_value(self)
}
fn to_bytes(&self) -> Result<Vec<u8>, SerializeError> {
TypedValue::from_value(self)?.to_bytes()
}
}
#[cfg(test)]
mod tests;