Skip to main content

sim_codec_binary_base64/
codec.rs

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