Skip to main content

sim_codec_json/
codec.rs

1//! The `JsonCodec` runtime object and its `Lib` registration.
2//!
3//! Wires the `Expr <-> JSON` conversions in this crate into the codec
4//! decoder/encoder, located, and tree traits so JSON becomes a registered
5//! codec surface in the runtime.
6
7use std::sync::Arc;
8
9use serde_json::Value as JsonValue;
10use sim_codec::{
11    CodecDefaultDecode, CodecRuntime, DecodeBudget, Decoder, Encoder, Input, LocatedDecoder,
12    LocatedEncoder, Output, ReadCx, TreeDecoder, TreeEncoder, codec_value, validate_expr_tree,
13};
14use sim_kernel::{
15    AbiVersion, DefaultFactory, Dependency, Error, Export, Expr, Lib, LibManifest, LibTarget,
16    Linker, LocatedExpr, LocatedExprTree, Result, Symbol, Version, WriteCx,
17};
18
19use crate::{
20    expr_to_json, json_to_expr, json_to_located_expr, json_to_tree, located_expr_to_json,
21    tree_to_json,
22};
23
24/// JSON codec runtime object that round-trips every [`Expr`] through JSON.
25///
26/// Implements all codec roles -- [`Decoder`]/[`Encoder`], the located
27/// [`LocatedDecoder`]/[`LocatedEncoder`], and the tree
28/// [`TreeDecoder`]/[`TreeEncoder`] -- by projecting the shared `Expr` graph onto
29/// `$expr`-tagged (and `$located`) `serde_json::Value` forms, so any expression
30/// the kernel can hold survives a JSON round-trip losslessly.
31pub struct JsonCodec;
32
33impl Decoder for JsonCodec {
34    fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
35        let source = input.into_string()?;
36        let mut budget = DecodeBudget::new(cx.limits);
37        budget.check_input_bytes(cx.codec, source.len())?;
38        let value =
39            serde_json::from_str::<JsonValue>(&source).map_err(|err| Error::CodecError {
40                codec: cx.codec,
41                message: err.to_string(),
42            })?;
43        json_to_expr(cx.codec, &value, &mut budget, 0)
44    }
45}
46
47impl Encoder for JsonCodec {
48    fn encode(&self, cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
49        let value = expr_to_json(expr);
50        let text = serde_json::to_string(&value).map_err(|err| Error::CodecError {
51            codec: cx.codec,
52            message: err.to_string(),
53        })?;
54        Ok(Output::Text(text))
55    }
56}
57
58impl LocatedDecoder for JsonCodec {
59    fn decode_located(
60        &self,
61        cx: &mut ReadCx<'_>,
62        input: Input,
63        _source_id: String,
64    ) -> Result<LocatedExpr> {
65        let source = input.into_string()?;
66        let mut budget = DecodeBudget::new(cx.limits);
67        budget.check_input_bytes(cx.codec, source.len())?;
68        let value =
69            serde_json::from_str::<JsonValue>(&source).map_err(|err| Error::CodecError {
70                codec: cx.codec,
71                message: err.to_string(),
72            })?;
73        json_to_located_expr(cx.codec, &value, &mut budget, 0)
74    }
75}
76
77impl LocatedEncoder for JsonCodec {
78    fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output> {
79        let value = located_expr_to_json(expr, cx.options.lossless_origin);
80        let text = serde_json::to_string(&value).map_err(|err| Error::CodecError {
81            codec: cx.codec,
82            message: err.to_string(),
83        })?;
84        Ok(Output::Text(text))
85    }
86}
87
88impl TreeDecoder for JsonCodec {
89    fn decode_tree(
90        &self,
91        cx: &mut ReadCx<'_>,
92        input: Input,
93        _source_id: String,
94    ) -> Result<LocatedExprTree> {
95        let source = input.into_string()?;
96        let mut budget = DecodeBudget::new(cx.limits);
97        budget.check_input_bytes(cx.codec, source.len())?;
98        let value =
99            serde_json::from_str::<JsonValue>(&source).map_err(|err| Error::CodecError {
100                codec: cx.codec,
101                message: err.to_string(),
102            })?;
103        json_to_tree(cx.codec, &value, &mut budget, 0)
104    }
105}
106
107impl TreeEncoder for JsonCodec {
108    fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
109        validate_expr_tree(cx.codec, expr)?;
110        let value = tree_to_json(expr, cx.options.lossless_origin);
111        let text = serde_json::to_string(&value).map_err(|err| Error::CodecError {
112            codec: cx.codec,
113            message: err.to_string(),
114        })?;
115        Ok(Output::Text(text))
116    }
117}
118
119/// [`Lib`] that registers the JSON codec with the runtime.
120///
121/// Its manifest exports the `codec/json` codec, and loading wires a [`JsonCodec`]
122/// into the linker as the decode and encode surface for all codec roles.
123pub struct JsonCodecLib {
124    symbol: Symbol,
125    codec_id: sim_kernel::CodecId,
126}
127
128impl JsonCodecLib {
129    /// Creates the codec lib bound to the runtime-assigned `id` for `codec/json`.
130    pub fn new(id: sim_kernel::CodecId) -> Self {
131        Self {
132            symbol: Symbol::qualified("codec", "json"),
133            codec_id: id,
134        }
135    }
136}
137
138impl Lib for JsonCodecLib {
139    fn manifest(&self) -> LibManifest {
140        LibManifest {
141            id: self.symbol.clone(),
142            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
143            abi: AbiVersion { major: 0, minor: 1 },
144            target: LibTarget::HostRegistered,
145            requires: Vec::<Dependency>::new(),
146            capabilities: Vec::new(),
147            exports: vec![Export::Codec {
148                symbol: self.symbol.clone(),
149                codec_id: Some(self.codec_id),
150            }],
151        }
152    }
153
154    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
155        let _factory = DefaultFactory;
156        let expr_shape =
157            sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "JsonTaggedExpr"))?;
158        let options_shape = sim_codec::resolve_options_shape(linker)?;
159
160        linker.codec_value(
161            self.symbol.clone(),
162            codec_value(CodecRuntime {
163                id: self.codec_id,
164                symbol: self.symbol.clone(),
165                decoder: Some(Arc::new(JsonCodec)),
166                located_decoder: Some(Arc::new(JsonCodec)),
167                tree_decoder: Some(Arc::new(JsonCodec)),
168                encoder: Some(Arc::new(JsonCodec)),
169                located_encoder: Some(Arc::new(JsonCodec)),
170                tree_encoder: Some(Arc::new(JsonCodec)),
171                expr_shape,
172                options_shape,
173                default_decode: CodecDefaultDecode::Datum,
174            }),
175        )?;
176        Ok(())
177    }
178}