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::{CHUNK_FUNCTIONS, 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()?;
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()?;
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(CHUNK_FUNCTIONS.iter().map(|kind| Export::Function {
228 symbol: kind.symbol(),
229 function_id: None,
230 }));
231 LibManifest {
232 id: self.symbol.clone(),
233 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
234 abi: AbiVersion { major: 0, minor: 1 },
235 target: LibTarget::HostRegistered,
236 requires: Vec::<Dependency>::new(),
237 capabilities: Vec::new(),
238 exports,
239 }
240 }
241
242 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
243 let _factory = DefaultFactory;
244 let expr_shape =
245 sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "Document"))?;
246 let options_shape = sim_codec::resolve_options_shape(linker)?;
247
248 linker.codec_value(
249 self.symbol.clone(),
250 codec_value(CodecRuntime {
251 id: self.codec_id,
252 symbol: self.symbol.clone(),
253 decoder: Some(Arc::new(DocCodec)),
254 located_decoder: None,
255 tree_decoder: None,
256 encoder: Some(Arc::new(DocCodec)),
257 located_encoder: None,
258 tree_encoder: None,
259 expr_shape,
260 options_shape,
261 default_decode: CodecDefaultDecode::Datum,
262 }),
263 )?;
264
265 for kind in CHUNK_FUNCTIONS {
266 linker.function_value(
267 kind.symbol(),
268 cx.factory()
269 .opaque(Arc::new(DocChunkFunction::new(*kind)))?,
270 )?;
271 }
272 Ok(())
273 }
274}
275
276pub fn install_doc_codec(cx: &mut Cx) -> Result<()> {
279 install_markup_codecs(cx, default_backend_registry())?;
280 let lib = DocCodecLib::new(cx.registry_mut().fresh_codec_id());
281 cx.load_lib(&lib)?;
282 Ok(())
283}
284
285pub fn install_markup_codecs(cx: &mut Cx, registry: BackendRegistry) -> Result<()> {
287 if registry.is_empty() {
288 return Err(Error::Lib("markup backend registry is empty".to_owned()));
289 }
290 let lib = MarkupCodecLib::new(cx, registry);
291 cx.load_lib(&lib)?;
292 Ok(())
293}