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