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