1use crate::{
4 JAVASCRIPT_CODEC_ID, decode_javascript, decode_javascript_located, decode_javascript_tree,
5 encode_javascript,
6};
7use sim_codec::{
8 CodecDefaultDecode, CodecRuntime, DecodeBudget, Decoder, Encoder, Input, LocatedDecoder,
9 Output, ReadCx, TreeDecoder, TreeEncoder, codec_value, validate_expr_tree,
10};
11use sim_kernel::{
12 AbiVersion, Dependency, Error, Export, Expr, Lib, LibManifest, LibTarget, Linker, LocatedExpr,
13 LocatedExprTree, Result, Symbol, Version, WriteCx,
14};
15use std::sync::Arc;
16
17#[derive(Default)]
19pub struct JavascriptCodec;
20
21impl Decoder for JavascriptCodec {
22 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
23 let source = input_text(cx.codec, input)?;
24 let mut budget = DecodeBudget::new(cx.limits);
25 decode_javascript(cx, &source, &mut budget)
26 }
27}
28impl LocatedDecoder for JavascriptCodec {
29 fn decode_located(
30 &self,
31 cx: &mut ReadCx<'_>,
32 input: Input,
33 source_id: String,
34 ) -> Result<LocatedExpr> {
35 decode_javascript_located(cx, source_id, input)
36 }
37}
38impl TreeDecoder for JavascriptCodec {
39 fn decode_tree(
40 &self,
41 cx: &mut ReadCx<'_>,
42 input: Input,
43 source_id: String,
44 ) -> Result<LocatedExprTree> {
45 decode_javascript_tree(cx, source_id, input)
46 }
47}
48impl Encoder for JavascriptCodec {
49 fn encode(&self, _cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
50 encode_javascript(expr)
51 }
52}
53impl TreeEncoder for JavascriptCodec {
54 fn encode_tree(&self, cx: &mut WriteCx<'_>, tree: &LocatedExprTree) -> Result<Output> {
55 validate_expr_tree(cx.codec, tree)?;
56 if cx.options.lossless_origin
57 && let Some(origin) = &tree.origin
58 && let Some(bytes) = cx.cx.sources().slice(origin)
59 {
60 return Ok(Output::Text(
61 std::str::from_utf8(bytes)
62 .map_err(|error| Error::CodecError {
63 codec: cx.codec,
64 message: format!("javascript source origin is not UTF-8: {error}"),
65 })?
66 .to_owned(),
67 ));
68 }
69 encode_javascript(&tree.expr)
70 }
71}
72
73pub struct JavascriptCodecLib {
75 symbol: Symbol,
76 codec_id: sim_kernel::CodecId,
77}
78impl JavascriptCodecLib {
79 #[must_use]
81 pub fn new(codec_id: sim_kernel::CodecId) -> Self {
82 Self {
83 symbol: Symbol::qualified("codec", "javascript"),
84 codec_id,
85 }
86 }
87}
88impl Default for JavascriptCodecLib {
89 fn default() -> Self {
90 Self::new(JAVASCRIPT_CODEC_ID)
91 }
92}
93impl Lib for JavascriptCodecLib {
94 fn manifest(&self) -> LibManifest {
95 LibManifest {
96 id: self.symbol.clone(),
97 version: Version(env!("CARGO_PKG_VERSION").into()),
98 abi: AbiVersion { major: 0, minor: 1 },
99 target: LibTarget::HostRegistered,
100 requires: Vec::<Dependency>::new(),
101 capabilities: Vec::new(),
102 exports: vec![Export::Codec {
103 symbol: self.symbol.clone(),
104 codec_id: Some(self.codec_id),
105 }],
106 }
107 }
108 fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
109 let expr_shape = sim_codec::resolve_expr_shape(
110 linker,
111 &Symbol::qualified("codec", "JavaScriptSurface"),
112 )?;
113 let options_shape = sim_codec::resolve_options_shape(linker)?;
114 let codec = Arc::new(JavascriptCodec);
115 linker.codec_value(
116 self.symbol.clone(),
117 codec_value(CodecRuntime {
118 id: self.codec_id,
119 symbol: self.symbol.clone(),
120 decoder: Some(codec.clone()),
121 located_decoder: Some(codec.clone()),
122 tree_decoder: Some(codec.clone()),
123 encoder: Some(codec.clone()),
124 located_encoder: None,
125 tree_encoder: Some(codec),
126 expr_shape,
127 options_shape,
128 default_decode: CodecDefaultDecode::TermInEvalDatumOtherwise,
129 }),
130 )?;
131 Ok(())
132 }
133}
134
135fn input_text(codec: sim_kernel::CodecId, input: Input) -> Result<String> {
136 match input {
137 Input::Text(text) => Ok(text),
138 Input::Bytes(bytes) => String::from_utf8(bytes).map_err(|error| Error::CodecError {
139 codec,
140 message: format!("codec input is not valid UTF-8: {error}"),
141 }),
142 }
143}