1use std::sync::Arc;
8
9use sim_codec::{
10 CodecDefaultDecode, CodecRuntime, DecodeBudget, Decoder, Encoder, Input, Output, ReadCx,
11 codec_value,
12};
13use sim_kernel::{
14 AbiVersion, Cx, DefaultFactory, Dependency, Error, Export, Lib, LibManifest, LibTarget, Linker,
15 Result, Symbol, Version, WriteCx,
16};
17
18use crate::backend::{
19 BackendRegistry, MarkupBackend, MarkupDecodeOptions, MarkupEncodeOptions, MarkupError,
20 default_backend_registry,
21};
22use crate::document::DocValue;
23use crate::functions::{CATALOG_FUNCTIONS, CHUNK_FUNCTIONS, DocCatalogFunction, DocChunkFunction};
24use crate::markdown::MarkdownBackend;
25use crate::markup::MarkupDoc;
26
27pub const MARKUP_CODEC_PREFIX: &str = "codec/markup/";
29
30pub fn markup_codec_symbol(id: &crate::BackendId) -> Symbol {
32 Symbol::qualified("codec", format!("markup/{}", id.as_str()))
33}
34
35pub struct MarkupCodec {
37 backend: Arc<dyn MarkupBackend>,
38}
39
40impl MarkupCodec {
41 pub fn new(backend: Arc<dyn MarkupBackend>) -> Self {
43 Self { backend }
44 }
45
46 pub fn backend_id(&self) -> crate::BackendId {
48 self.backend.id()
49 }
50}
51
52impl Decoder for MarkupCodec {
53 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<sim_kernel::Expr> {
54 let source = input.into_string_for(cx.codec)?;
55 let budget = DecodeBudget::new(cx.limits);
56 budget.check_input_bytes(cx.codec, source.len())?;
57 let (doc, _fidelity) = self
58 .backend
59 .decode(&source, &MarkupDecodeOptions::default())
60 .map_err(|err| err.into_kernel_error(cx.codec))?;
61 Ok(doc.as_expr())
62 }
63}
64
65impl Encoder for MarkupCodec {
66 fn encode(&self, cx: &mut WriteCx<'_>, expr: &sim_kernel::Expr) -> Result<Output> {
67 let doc = MarkupDoc::from_expr(expr).map_err(|err| {
68 MarkupError::InvalidDocument(err.to_string()).into_kernel_error(cx.codec)
69 })?;
70 let (source, fidelity) = self
71 .backend
72 .encode(&doc, &MarkupEncodeOptions::default())
73 .map_err(|err| err.into_kernel_error(cx.codec))?;
74 if !fidelity.dropped.is_empty() {
75 return Err(MarkupError::Encode(format!(
76 "backend {} reported {} dropped part(s)",
77 fidelity.backend,
78 fidelity.dropped.len()
79 ))
80 .into_kernel_error(cx.codec));
81 }
82 Ok(Output::Text(source))
83 }
84}
85
86pub struct DocCodec;
92
93impl Decoder for DocCodec {
94 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<sim_kernel::Expr> {
95 let source = input.into_string_for(cx.codec)?;
96 let budget = DecodeBudget::new(cx.limits);
97 budget.check_input_bytes(cx.codec, source.len())?;
98 let (doc, _fidelity) = MarkdownBackend
99 .decode(&source, &MarkupDecodeOptions::default())
100 .map_err(|err| err.into_kernel_error(cx.codec))?;
101 Ok(doc.as_expr())
102 }
103}
104
105impl Encoder for DocCodec {
106 fn encode(&self, cx: &mut WriteCx<'_>, expr: &sim_kernel::Expr) -> Result<Output> {
107 match expr {
108 sim_kernel::Expr::String(text) => Ok(Output::Text(text.clone())),
109 sim_kernel::Expr::Map(_) => Ok(Output::Text(encode_doc_expr(expr).map_err(|err| {
110 Error::CodecError {
111 codec: cx.codec,
112 message: err.to_string(),
113 }
114 })?)),
115 _ => Err(Error::CodecError {
116 codec: cx.codec,
117 message: "codec:doc encodes document values or strings".to_owned(),
118 }),
119 }
120 }
121}
122
123fn encode_doc_expr(expr: &sim_kernel::Expr) -> Result<String> {
124 match MarkupDoc::from_expr(expr) {
125 Ok(doc) => Ok(doc.to_source_text()),
126 Err(markup_error) => match DocValue::from_expr(expr) {
127 Ok(doc) => Ok(doc.text),
128 Err(_) => Err(markup_error),
129 },
130 }
131}
132
133struct RegisteredBackend {
134 backend: Arc<dyn MarkupBackend>,
135 codec_id: sim_kernel::CodecId,
136}
137
138struct MarkupCodecLib {
139 backends: Vec<RegisteredBackend>,
140}
141
142impl MarkupCodecLib {
143 fn new(cx: &mut Cx, registry: BackendRegistry) -> Self {
144 let backends = registry
145 .iter()
146 .map(|(_, backend)| RegisteredBackend {
147 backend: backend.clone(),
148 codec_id: cx.registry_mut().fresh_codec_id(),
149 })
150 .collect();
151 Self { backends }
152 }
153}
154
155impl Lib for MarkupCodecLib {
156 fn manifest(&self) -> LibManifest {
157 LibManifest {
158 id: Symbol::qualified("codec", "markup"),
159 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
160 abi: AbiVersion { major: 0, minor: 1 },
161 target: LibTarget::HostRegistered,
162 requires: Vec::<Dependency>::new(),
163 capabilities: Vec::new(),
164 exports: self
165 .backends
166 .iter()
167 .map(|registered| Export::Codec {
168 symbol: markup_codec_symbol(®istered.backend.id()),
169 codec_id: Some(registered.codec_id),
170 })
171 .collect(),
172 }
173 }
174
175 fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
176 let expr_shape =
177 sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "Document"))?;
178 let options_shape = sim_codec::resolve_options_shape(linker)?;
179
180 for registered in &self.backends {
181 let symbol = markup_codec_symbol(®istered.backend.id());
182 linker.codec_value(
183 symbol.clone(),
184 codec_value(CodecRuntime {
185 id: registered.codec_id,
186 symbol,
187 decoder: Some(Arc::new(MarkupCodec::new(registered.backend.clone()))),
188 located_decoder: None,
189 tree_decoder: None,
190 encoder: Some(Arc::new(MarkupCodec::new(registered.backend.clone()))),
191 located_encoder: None,
192 tree_encoder: None,
193 expr_shape: expr_shape.clone(),
194 options_shape: options_shape.clone(),
195 default_decode: CodecDefaultDecode::Datum,
196 }),
197 )?;
198 }
199 Ok(())
200 }
201}
202
203pub struct DocCodecLib {
206 symbol: Symbol,
207 codec_id: sim_kernel::CodecId,
208}
209
210impl DocCodecLib {
211 pub fn new(id: sim_kernel::CodecId) -> Self {
214 Self {
215 symbol: Symbol::qualified("codec", "doc"),
216 codec_id: id,
217 }
218 }
219}
220
221impl Lib for DocCodecLib {
222 fn manifest(&self) -> LibManifest {
223 let mut exports = vec![Export::Codec {
224 symbol: self.symbol.clone(),
225 codec_id: Some(self.codec_id),
226 }];
227 exports.extend(
228 CHUNK_FUNCTIONS
229 .iter()
230 .map(|kind| Export::Function {
231 symbol: kind.symbol(),
232 function_id: None,
233 })
234 .chain(CATALOG_FUNCTIONS.iter().map(|kind| Export::Function {
235 symbol: kind.symbol(),
236 function_id: None,
237 })),
238 );
239 LibManifest {
240 id: self.symbol.clone(),
241 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
242 abi: AbiVersion { major: 0, minor: 1 },
243 target: LibTarget::HostRegistered,
244 requires: Vec::<Dependency>::new(),
245 capabilities: Vec::new(),
246 exports,
247 }
248 }
249
250 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
251 let _factory = DefaultFactory;
252 let expr_shape =
253 sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "Document"))?;
254 let options_shape = sim_codec::resolve_options_shape(linker)?;
255
256 linker.codec_value(
257 self.symbol.clone(),
258 codec_value(CodecRuntime {
259 id: self.codec_id,
260 symbol: self.symbol.clone(),
261 decoder: Some(Arc::new(DocCodec)),
262 located_decoder: None,
263 tree_decoder: None,
264 encoder: Some(Arc::new(DocCodec)),
265 located_encoder: None,
266 tree_encoder: None,
267 expr_shape,
268 options_shape,
269 default_decode: CodecDefaultDecode::Datum,
270 }),
271 )?;
272
273 for kind in CHUNK_FUNCTIONS {
274 linker.function_value(
275 kind.symbol(),
276 cx.factory()
277 .opaque(Arc::new(DocChunkFunction::new(*kind)))?,
278 )?;
279 }
280 for kind in CATALOG_FUNCTIONS {
281 linker.function_value(
282 kind.symbol(),
283 cx.factory()
284 .opaque(Arc::new(DocCatalogFunction::new(*kind)))?,
285 )?;
286 }
287 Ok(())
288 }
289}
290
291pub fn install_doc_codec(cx: &mut Cx) -> Result<()> {
294 install_markup_codecs(cx, default_backend_registry())?;
295 let lib = DocCodecLib::new(cx.registry_mut().fresh_codec_id());
296 cx.load_lib(&lib)?;
297 Ok(())
298}
299
300pub fn install_markup_codecs(cx: &mut Cx, registry: BackendRegistry) -> Result<()> {
302 if registry.is_empty() {
303 return Err(Error::Lib("markup backend registry is empty".to_owned()));
304 }
305 let lib = MarkupCodecLib::new(cx, registry);
306 cx.load_lib(&lib)?;
307 Ok(())
308}