use std::sync::Arc;
use sim_kernel::{
ClassRef, CodecId, Cx, LocatedExpr, LocatedExprTree, Object, Result, ShapeRef, Symbol, Value,
WriteCx,
};
use super::limits::ReadCx;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Input {
Text(String),
Bytes(Vec<u8>),
}
impl Input {
pub fn into_string(self) -> Result<String> {
match self {
Self::Text(text) => Ok(text),
Self::Bytes(bytes) => {
String::from_utf8(bytes).map_err(|err| sim_kernel::Error::CodecError {
codec: CodecId(0),
message: err.to_string(),
})
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Output {
Text(String),
Bytes(Vec<u8>),
}
impl Output {
pub fn into_text(self) -> Result<String> {
match self {
Self::Text(text) => Ok(text),
Self::Bytes(bytes) => {
String::from_utf8(bytes).map_err(|err| sim_kernel::Error::CodecError {
codec: CodecId(0),
message: err.to_string(),
})
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DecodePosition {
Eval,
Quote,
Data,
Pattern,
}
impl From<sim_kernel::EncodePosition> for DecodePosition {
fn from(position: sim_kernel::EncodePosition) -> Self {
match position {
sim_kernel::EncodePosition::Eval => Self::Eval,
sim_kernel::EncodePosition::Quote => Self::Quote,
sim_kernel::EncodePosition::Data => Self::Data,
sim_kernel::EncodePosition::Pattern => Self::Pattern,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DecodeTarget {
Datum,
Term,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CodecDefaultDecode {
Datum,
TermInEvalDatumOtherwise,
}
impl CodecDefaultDecode {
pub fn target_for(self, position: DecodePosition) -> DecodeTarget {
match (self, position) {
(Self::TermInEvalDatumOtherwise, DecodePosition::Eval) => DecodeTarget::Term,
_ => DecodeTarget::Datum,
}
}
pub fn as_symbol_name(self) -> &'static str {
match self {
Self::Datum => "datum",
Self::TermInEvalDatumOtherwise => "term-in-eval-datum-otherwise",
}
}
}
pub trait Decoder: Send + Sync {
fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<sim_kernel::Expr>;
}
pub trait LocatedDecoder: Send + Sync {
fn decode_located(
&self,
cx: &mut ReadCx<'_>,
input: Input,
source_id: String,
) -> Result<LocatedExpr>;
}
pub trait TreeDecoder: Send + Sync {
fn decode_tree(
&self,
cx: &mut ReadCx<'_>,
input: Input,
source_id: String,
) -> Result<LocatedExprTree>;
}
pub trait Encoder: Send + Sync {
fn encode(&self, cx: &mut WriteCx<'_>, expr: &sim_kernel::Expr) -> Result<Output>;
}
pub trait LocatedEncoder: Send + Sync {
fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output>;
}
pub trait TreeEncoder: Send + Sync {
fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output>;
}
#[sim_citizen_derive::non_citizen(
reason = "codec runtime registry handle; reconstruct by loading the codec lib and using the codec symbol",
kind = "handle",
descriptor = "core/Codec"
)]
pub struct CodecRuntime {
pub id: CodecId,
pub symbol: Symbol,
pub decoder: Option<Arc<dyn Decoder>>,
pub located_decoder: Option<Arc<dyn LocatedDecoder>>,
pub tree_decoder: Option<Arc<dyn TreeDecoder>>,
pub encoder: Option<Arc<dyn Encoder>>,
pub located_encoder: Option<Arc<dyn LocatedEncoder>>,
pub tree_encoder: Option<Arc<dyn TreeEncoder>>,
pub expr_shape: ShapeRef,
pub options_shape: ShapeRef,
pub default_decode: CodecDefaultDecode,
}
impl CodecRuntime {
pub fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<sim_kernel::Expr> {
let Some(decoder) = &self.decoder else {
return Err(sim_kernel::Error::CodecError {
codec: self.id,
message: format!("codec {} has no decoder", self.symbol),
});
};
decoder.decode(cx, input)
}
pub fn encode(&self, cx: &mut WriteCx<'_>, expr: &sim_kernel::Expr) -> Result<Output> {
let Some(encoder) = &self.encoder else {
return Err(sim_kernel::Error::CodecError {
codec: self.id,
message: format!("codec {} has no encoder", self.symbol),
});
};
encoder.encode(cx, expr)
}
pub fn decode_located(
&self,
cx: &mut ReadCx<'_>,
input: Input,
source_id: String,
) -> Result<LocatedExpr> {
if let Some(decoder) = &self.located_decoder {
return decoder.decode_located(cx, input, source_id);
}
Ok(LocatedExpr {
expr: self.decode(cx, input)?,
origin: None,
})
}
pub fn decode_tree(
&self,
cx: &mut ReadCx<'_>,
input: Input,
source_id: String,
) -> Result<LocatedExprTree> {
if let Some(decoder) = &self.tree_decoder {
return decoder.decode_tree(cx, input, source_id);
}
let located = self.decode_located(cx, input, source_id)?;
let mut tree = LocatedExprTree::from_expr_recursive(located.expr.clone());
tree.origin = located.origin;
Ok(tree)
}
pub fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output> {
if cx.options.lossless_origin
&& let Some(encoder) = &self.located_encoder
{
return encoder.encode_located(cx, expr);
}
self.encode(cx, &expr.expr)
}
pub fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
if cx.options.lossless_origin {
if let Some(encoder) = &self.tree_encoder {
return encoder.encode_tree(cx, expr);
}
if let Some(encoder) = &self.located_encoder {
return encoder.encode_located(cx, &expr.located());
}
}
self.encode(cx, &expr.expr)
}
}
impl Object for CodecRuntime {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<codec {}>", self.symbol))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl sim_kernel::ObjectCompat for CodecRuntime {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
if let Some(value) = cx
.registry()
.class_by_symbol(&Symbol::qualified("core", "Codec"))
{
return Ok(value.clone());
}
cx.factory().class_stub(
sim_kernel::CORE_CODEC_CLASS_ID,
Symbol::qualified("core", "Codec"),
)
}
fn as_expr(&self, _cx: &mut Cx) -> Result<sim_kernel::Expr> {
Ok(sim_kernel::Expr::Symbol(self.symbol.clone()))
}
fn as_table(&self, cx: &mut Cx) -> Result<Value> {
cx.factory().table(vec![
(
Symbol::new("symbol"),
cx.factory().string(self.symbol.to_string())?,
),
(
Symbol::new("has-decoder"),
cx.factory().bool(self.decoder.is_some())?,
),
(
Symbol::new("has-encoder"),
cx.factory().bool(self.encoder.is_some())?,
),
(
Symbol::new("has-located-decoder"),
cx.factory().bool(self.located_decoder.is_some())?,
),
(
Symbol::new("has-located-encoder"),
cx.factory().bool(self.located_encoder.is_some())?,
),
(
Symbol::new("has-tree-decoder"),
cx.factory().bool(self.tree_decoder.is_some())?,
),
(
Symbol::new("has-tree-encoder"),
cx.factory().bool(self.tree_encoder.is_some())?,
),
(
Symbol::new("default-decode"),
cx.factory()
.string(self.default_decode.as_symbol_name().to_owned())?,
),
(Symbol::new("expr-shape"), self.expr_shape.clone()),
(Symbol::new("options-shape"), self.options_shape.clone()),
])
}
}