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_with_limits, encode_base64};
18use crate::cookbook::{BinaryBase64RoundtripReport, roundtrip_report_symbol};
19
20pub struct BinaryBase64Codec;
29
30impl Decoder for BinaryBase64Codec {
31 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
32 decode_tree(cx, input).map(|tree| tree.located().expr)
33 }
34}
35
36impl Encoder for BinaryBase64Codec {
37 fn encode(&self, _cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
38 let frame = sim_codec_binary::encode_frame(expr)?;
39 Ok(Output::Text(encode_base64(&frame.0)))
40 }
41}
42
43impl LocatedDecoder for BinaryBase64Codec {
44 fn decode_located(
45 &self,
46 cx: &mut ReadCx<'_>,
47 input: Input,
48 _source_id: String,
49 ) -> Result<LocatedExpr> {
50 decode_tree(cx, input).map(|tree| tree.located())
51 }
52}
53
54impl LocatedEncoder for BinaryBase64Codec {
55 fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output> {
56 let frame = sim_codec_binary::encode_located_frame(expr, cx.options.lossless_origin)?;
57 Ok(Output::Text(encode_base64(&frame.0)))
58 }
59}
60
61impl TreeDecoder for BinaryBase64Codec {
62 fn decode_tree(
63 &self,
64 cx: &mut ReadCx<'_>,
65 input: Input,
66 _source_id: String,
67 ) -> Result<LocatedExprTree> {
68 decode_tree(cx, input)
69 }
70}
71
72impl TreeEncoder for BinaryBase64Codec {
73 fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
74 validate_expr_tree(cx.codec, expr)?;
75 let frame = sim_codec_binary::encode_located_tree_frame(expr, cx.options.lossless_origin)?;
76 Ok(Output::Text(encode_base64(&frame.0)))
77 }
78}
79
80fn decode_tree(cx: &mut ReadCx<'_>, input: Input) -> Result<LocatedExprTree> {
81 let text = input_text(cx.codec, input)?;
82 let bytes = decode_base64_with_limits(cx.codec, &text, cx.limits)?;
83 sim_codec_binary::decode_located_tree_frame_with_limits(
84 cx.codec,
85 &bytes,
86 sim_codec_binary::DecodeLimits::from(cx.limits),
87 )
88 .map(|(_, tree)| tree)
89}
90
91fn input_text(codec: CodecId, input: Input) -> Result<String> {
92 match input {
93 Input::Text(text) => Ok(text),
94 Input::Bytes(bytes) => String::from_utf8(bytes).map_err(|err| Error::CodecError {
95 codec,
96 message: format!("binary-base64 input is not valid UTF-8: {err}"),
97 }),
98 }
99}
100
101pub struct BinaryBase64CodecLib {
107 symbol: Symbol,
108 codec_id: CodecId,
109}
110
111impl BinaryBase64CodecLib {
112 pub fn new(id: CodecId) -> Self {
115 Self {
116 symbol: Symbol::qualified("codec", "binary-base64"),
117 codec_id: id,
118 }
119 }
120}
121
122impl Lib for BinaryBase64CodecLib {
123 fn manifest(&self) -> LibManifest {
124 LibManifest {
125 id: self.symbol.clone(),
126 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
127 abi: AbiVersion { major: 0, minor: 1 },
128 target: LibTarget::HostRegistered,
129 requires: Vec::<Dependency>::new(),
130 capabilities: Vec::new(),
131 exports: vec![
132 Export::Codec {
133 symbol: self.symbol.clone(),
134 codec_id: Some(self.codec_id),
135 },
136 Export::Function {
137 symbol: roundtrip_report_symbol(),
138 function_id: None,
139 },
140 ],
141 }
142 }
143
144 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
145 let _factory = DefaultFactory;
146 let expr_shape =
147 sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "BinaryBase64Text"))?;
148 let options_shape = sim_codec::resolve_options_shape(linker)?;
149
150 linker.codec_value(
151 self.symbol.clone(),
152 codec_value(CodecRuntime {
153 id: self.codec_id,
154 symbol: self.symbol.clone(),
155 decoder: Some(Arc::new(BinaryBase64Codec)),
156 located_decoder: Some(Arc::new(BinaryBase64Codec)),
157 tree_decoder: Some(Arc::new(BinaryBase64Codec)),
158 encoder: Some(Arc::new(BinaryBase64Codec)),
159 located_encoder: Some(Arc::new(BinaryBase64Codec)),
160 tree_encoder: Some(Arc::new(BinaryBase64Codec)),
161 expr_shape,
162 options_shape,
163 default_decode: CodecDefaultDecode::Datum,
164 }),
165 )?;
166 linker.function_value(
167 roundtrip_report_symbol(),
168 cx.factory().opaque(Arc::new(BinaryBase64RoundtripReport))?,
169 )?;
170 Ok(())
171 }
172}