use sim_kernel::{Error, Expr, Result, Symbol};
use crate::{TablePath, decode_table_op};
pub mod entries {
use super::*;
pub fn encode(entries: &[(Symbol, Expr)]) -> Expr {
Expr::Map(
entries
.iter()
.map(|(key, value)| (Expr::Symbol(key.clone()), value.clone()))
.collect(),
)
}
pub fn decode(expr: &Expr) -> Result<Vec<(Symbol, Expr)>> {
let Expr::Map(entries) = expr else {
return Err(field_error("entries", "expected map"));
};
entries
.iter()
.map(|(key, value)| {
let Expr::Symbol(key) = key else {
return Err(field_error("entries", "expected symbol key"));
};
Ok((key.clone(), value.clone()))
})
.collect()
}
}
pub mod expr_list {
use super::*;
pub fn encode(items: &[Expr]) -> Expr {
Expr::List(items.to_vec())
}
pub fn decode(expr: &Expr) -> Result<Vec<Expr>> {
let Expr::List(items) = expr else {
return Err(field_error("items", "expected list"));
};
Ok(items.clone())
}
}
pub mod symbol {
use super::*;
pub fn encode(symbol: &Symbol) -> Expr {
Expr::Symbol(symbol.clone())
}
pub fn decode(expr: &Expr) -> Result<Symbol> {
let Expr::Symbol(symbol) = expr else {
return Err(field_error("symbol", "expected symbol"));
};
Ok(symbol.clone())
}
}
pub mod path_segments {
use super::*;
pub fn encode(segments: &[String]) -> Expr {
Expr::List(
segments
.iter()
.map(|segment| Expr::String(segment.clone()))
.collect(),
)
}
pub fn decode(expr: &Expr) -> Result<Vec<String>> {
let Expr::List(items) = expr else {
return Err(field_error("path", "expected list"));
};
let mut path = TablePath::new();
for item in items {
let Expr::String(segment) = item else {
return Err(field_error("path", "expected string segment"));
};
path.push(segment)
.map_err(|_| field_error("path", format!("illegal segment {segment:?}")))?;
}
Ok(path.segments().to_vec())
}
}
pub mod table_op_expr {
use super::*;
pub fn encode(op: &Expr) -> Expr {
op.clone()
}
pub fn decode(expr: &Expr) -> Result<Expr> {
decode_table_op(expr)
.map_err(|err| field_error("operation", format!("invalid table operation: {err:?}")))?;
Ok(expr.clone())
}
}
fn field_error(field: &str, message: impl Into<String>) -> Error {
Error::Eval(format!("table citizen field {field}: {}", message.into()))
}