Skip to main content

sim_codec_javascript/
codec.rs

1//! Loadable `codec/javascript` runtime object.
2
3use 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/// Runtime codec object for `codec/javascript`.
18#[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
73/// Host-registered library installing `codec/javascript`.
74pub struct JavascriptCodecLib {
75    symbol: Symbol,
76    codec_id: sim_kernel::CodecId,
77}
78impl JavascriptCodecLib {
79    /// Construct with a host-assigned codec id.
80    #[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}