1use std::sync::Arc;
6
7use sim_codec::{
8 CodecDefaultDecode, CodecRuntime, DecodeBudget, Decoder, Encoder, Input, Output, ReadCx,
9 codec_value,
10};
11use sim_kernel::{
12 AbiVersion, Cx, DefaultFactory, Dependency, Error, Export, Lib, LibManifest, LibTarget, Linker,
13 Result, Symbol, Version, WriteCx,
14};
15
16use crate::document::{DocValue, decode_document};
17use crate::functions::{CHUNK_FUNCTIONS, DocChunkFunction};
18
19pub struct DocCodec;
25
26impl Decoder for DocCodec {
27 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<sim_kernel::Expr> {
28 let source = input.into_string()?;
29 let budget = DecodeBudget::new(cx.limits);
30 budget.check_input_bytes(cx.codec, source.len())?;
31 Ok(decode_document(&source).as_expr())
32 }
33}
34
35impl Encoder for DocCodec {
36 fn encode(&self, cx: &mut WriteCx<'_>, expr: &sim_kernel::Expr) -> Result<Output> {
37 match expr {
38 sim_kernel::Expr::String(text) => Ok(Output::Text(text.clone())),
39 sim_kernel::Expr::Map(_) => {
40 let doc = DocValue::from_expr(expr).map_err(|err| Error::CodecError {
41 codec: cx.codec,
42 message: err.to_string(),
43 })?;
44 Ok(Output::Text(doc.text))
45 }
46 _ => Err(Error::CodecError {
47 codec: cx.codec,
48 message: "codec:doc encodes document values or strings".to_owned(),
49 }),
50 }
51 }
52}
53
54pub struct DocCodecLib {
57 symbol: Symbol,
58 codec_id: sim_kernel::CodecId,
59}
60
61impl DocCodecLib {
62 pub fn new(id: sim_kernel::CodecId) -> Self {
65 Self {
66 symbol: Symbol::qualified("codec", "doc"),
67 codec_id: id,
68 }
69 }
70}
71
72impl Lib for DocCodecLib {
73 fn manifest(&self) -> LibManifest {
74 let mut exports = vec![Export::Codec {
75 symbol: self.symbol.clone(),
76 codec_id: Some(self.codec_id),
77 }];
78 exports.extend(CHUNK_FUNCTIONS.iter().map(|kind| Export::Function {
79 symbol: kind.symbol(),
80 function_id: None,
81 }));
82 LibManifest {
83 id: self.symbol.clone(),
84 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
85 abi: AbiVersion { major: 0, minor: 1 },
86 target: LibTarget::HostRegistered,
87 requires: Vec::<Dependency>::new(),
88 capabilities: Vec::new(),
89 exports,
90 }
91 }
92
93 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
94 let _factory = DefaultFactory;
95 let expr_shape =
96 sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "Document"))?;
97 let options_shape = sim_codec::resolve_options_shape(linker)?;
98
99 linker.codec_value(
100 self.symbol.clone(),
101 codec_value(CodecRuntime {
102 id: self.codec_id,
103 symbol: self.symbol.clone(),
104 decoder: Some(Arc::new(DocCodec)),
105 located_decoder: None,
106 tree_decoder: None,
107 encoder: Some(Arc::new(DocCodec)),
108 located_encoder: None,
109 tree_encoder: None,
110 expr_shape,
111 options_shape,
112 default_decode: CodecDefaultDecode::Datum,
113 }),
114 )?;
115
116 for kind in CHUNK_FUNCTIONS {
117 linker.function_value(
118 kind.symbol(),
119 cx.factory()
120 .opaque(Arc::new(DocChunkFunction::new(*kind)))?,
121 )?;
122 }
123 Ok(())
124 }
125}
126
127pub fn install_doc_codec(cx: &mut Cx) -> Result<()> {
130 let lib = DocCodecLib::new(cx.registry_mut().fresh_codec_id());
131 cx.load_lib(&lib)?;
132 Ok(())
133}