Skip to main content

sim_codec_bitwise_base64/
codec.rs

1//! The `BitwiseBase64Codec` runtime object and its `Lib` registration.
2//!
3//! Implements the codec traits by delegating frame encode/decode to
4//! `sim-codec-bitwise` and adding the base64 text wrapping on either side. The
5//! base64 layer is shared from `sim-codec-binary-base64` rather than forked.
6
7use std::sync::Arc;
8
9use sim_codec::{
10    CodecDefaultDecode, CodecRuntime, Decoder, Encoder, Input, LocatedDecoder, LocatedEncoder,
11    Output, ReadCx, TreeDecoder, TreeEncoder, codec_value, validate_expr_tree,
12};
13use sim_codec_binary_base64::{decode_base64_with_limits, encode_base64};
14use sim_kernel::{
15    AbiVersion, CodecId, DefaultFactory, Dependency, Error, Export, Expr, Lib, LibManifest,
16    LibTarget, Linker, LocatedExpr, LocatedExprTree, Result, Symbol, Version, WriteCx,
17};
18
19use crate::cookbook::{BitwiseBase64RoundtripReport, roundtrip_report_symbol};
20
21/// Codec runtime object that carries `sim-codec-bitwise` frames as base64 text.
22///
23/// This domain codec is a thin text wrapper: it implements every codec role --
24/// [`Decoder`]/[`Encoder`], located [`LocatedDecoder`]/[`LocatedEncoder`], and
25/// tree [`TreeDecoder`]/[`TreeEncoder`] -- by delegating frame encode/decode to
26/// `sim-codec-bitwise` and base64-encoding the bytes on the way out and
27/// base64-decoding them on the way in. The base64 text and the underlying bytes
28/// are untrusted data; malformed input fails closed and is never executed.
29pub struct BitwiseBase64Codec;
30
31impl Decoder for BitwiseBase64Codec {
32    fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
33        decode_tree(cx, input).map(|tree| tree.located().expr)
34    }
35}
36
37impl Encoder for BitwiseBase64Codec {
38    fn encode(&self, _cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
39        let frame = sim_codec_bitwise::encode_frame(expr)?;
40        Ok(Output::Text(encode_base64(&frame.0)))
41    }
42}
43
44impl LocatedDecoder for BitwiseBase64Codec {
45    fn decode_located(
46        &self,
47        cx: &mut ReadCx<'_>,
48        input: Input,
49        _source_id: String,
50    ) -> Result<LocatedExpr> {
51        decode_tree(cx, input).map(|tree| tree.located())
52    }
53}
54
55impl LocatedEncoder for BitwiseBase64Codec {
56    fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output> {
57        let frame = sim_codec_bitwise::encode_located_frame(expr, cx.options.lossless_origin)?;
58        Ok(Output::Text(encode_base64(&frame.0)))
59    }
60}
61
62impl TreeDecoder for BitwiseBase64Codec {
63    fn decode_tree(
64        &self,
65        cx: &mut ReadCx<'_>,
66        input: Input,
67        _source_id: String,
68    ) -> Result<LocatedExprTree> {
69        decode_tree(cx, input)
70    }
71}
72
73impl TreeEncoder for BitwiseBase64Codec {
74    fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
75        validate_expr_tree(cx.codec, expr)?;
76        let frame = sim_codec_bitwise::encode_located_tree_frame(expr, cx.options.lossless_origin)?;
77        Ok(Output::Text(encode_base64(&frame.0)))
78    }
79}
80
81fn decode_tree(cx: &mut ReadCx<'_>, input: Input) -> Result<LocatedExprTree> {
82    let text = input_text(cx.codec, input)?;
83    let bytes = decode_base64_with_limits(cx.codec, &text, cx.limits)?;
84    sim_codec_bitwise::decode_located_tree_frame_with_limits(
85        cx.codec,
86        &bytes,
87        sim_codec_bitwise::DecodeLimits::from(cx.limits),
88    )
89    .map(|(_, tree)| tree)
90}
91
92fn input_text(codec: CodecId, input: Input) -> Result<String> {
93    match input {
94        Input::Text(text) => Ok(text),
95        Input::Bytes(bytes) => String::from_utf8(bytes).map_err(|err| Error::CodecError {
96            codec,
97            message: format!("bitwise-base64 input is not valid UTF-8: {err}"),
98        }),
99    }
100}
101
102/// [`Lib`] that registers the bitwise-base64 codec with the runtime.
103///
104/// Its manifest exports the `codec/bitwise-base64` codec, and loading wires a
105/// [`BitwiseBase64Codec`] into the linker as the decode and encode surface for
106/// all codec roles.
107pub struct BitwiseBase64CodecLib {
108    symbol: Symbol,
109    codec_id: CodecId,
110}
111
112impl BitwiseBase64CodecLib {
113    /// Creates the codec lib bound to the runtime-assigned `id` for
114    /// `codec/bitwise-base64`.
115    pub fn new(id: CodecId) -> Self {
116        Self {
117            symbol: Symbol::qualified("codec", "bitwise-base64"),
118            codec_id: id,
119        }
120    }
121}
122
123impl Lib for BitwiseBase64CodecLib {
124    fn manifest(&self) -> LibManifest {
125        LibManifest {
126            id: self.symbol.clone(),
127            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
128            abi: AbiVersion { major: 0, minor: 1 },
129            target: LibTarget::HostRegistered,
130            requires: Vec::<Dependency>::new(),
131            capabilities: Vec::new(),
132            exports: vec![
133                Export::Codec {
134                    symbol: self.symbol.clone(),
135                    codec_id: Some(self.codec_id),
136                },
137                Export::Function {
138                    symbol: roundtrip_report_symbol(),
139                    function_id: None,
140                },
141            ],
142        }
143    }
144
145    fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
146        let _factory = DefaultFactory;
147        let expr_shape = sim_codec::resolve_expr_shape(
148            linker,
149            &Symbol::qualified("codec", "BitwiseBase64Text"),
150        )?;
151        let options_shape = sim_codec::resolve_options_shape(linker)?;
152
153        linker.codec_value(
154            self.symbol.clone(),
155            codec_value(CodecRuntime {
156                id: self.codec_id,
157                symbol: self.symbol.clone(),
158                decoder: Some(Arc::new(BitwiseBase64Codec)),
159                located_decoder: Some(Arc::new(BitwiseBase64Codec)),
160                tree_decoder: Some(Arc::new(BitwiseBase64Codec)),
161                encoder: Some(Arc::new(BitwiseBase64Codec)),
162                located_encoder: Some(Arc::new(BitwiseBase64Codec)),
163                tree_encoder: Some(Arc::new(BitwiseBase64Codec)),
164                expr_shape,
165                options_shape,
166                default_decode: CodecDefaultDecode::Datum,
167            }),
168        )?;
169        linker.function_value(
170            roundtrip_report_symbol(),
171            cx.factory()
172                .opaque(Arc::new(BitwiseBase64RoundtripReport))?,
173        )?;
174        Ok(())
175    }
176}