1use 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, encode_base64};
18
19pub 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(cx.codec, &text)?;
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
100pub struct BinaryBase64CodecLib {
106 symbol: Symbol,
107 codec_id: CodecId,
108}
109
110impl BinaryBase64CodecLib {
111 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}