Skip to main content

sim_codec/implementation/
domain.rs

1//! Builder for domain codec libs.
2//!
3//! A "domain codec" lib (`codec:chat`, `codec:scene`, `codec:intent`, ...) was
4//! ~120 lines of near-identical scaffold: a UTF-8 `input_text` helper, a
5//! `Decoder`+`Encoder`, a `LibManifest` with one `Export::Codec`, and a
6//! `CodecRuntime` with ~10 fields mostly `None`. This collapses that to a few
7//! lines of glue: implement `Decoder`+`Encoder`, then build a [`DomainCodecLib`].
8
9use std::sync::Arc;
10
11use sim_kernel::{
12    AbiVersion, CodecId, DefaultFactory, Dependency, Export, Factory, Lib, LibManifest, LibTarget,
13    Linker, LoadCx, Result, ShapeRef, Symbol, Version,
14};
15
16use crate::{CodecDefaultDecode, CodecRuntime, Decoder, Encoder, Input, codec_value};
17
18/// Read a codec `Input` as UTF-8 text, tagging any error with `codec`. The
19/// shared body of every domain codec's `input_text` helper.
20pub fn domain_input_text(codec: CodecId, input: Input) -> Result<String> {
21    input.into_string_for(codec)
22}
23
24/// Resolve a general codec's expression shape: the `primary` symbol, then
25/// `core/Expr`, then `core/Any`, then nil. This is the shared fallback chain
26/// that every general-purpose codec (json/binary/binary-base64/algol/lisp/doc)
27/// hand-rolled identically before OVERLAP6.07.
28pub fn resolve_expr_shape(linker: &Linker, primary: &Symbol) -> Result<ShapeRef> {
29    let nil = DefaultFactory.nil()?;
30    Ok(linker
31        .registry()
32        .shape_by_symbol(primary)
33        .or_else(|| {
34            linker
35                .registry()
36                .shape_by_symbol(&Symbol::qualified("core", "Expr"))
37        })
38        .or_else(|| {
39            linker
40                .registry()
41                .shape_by_symbol(&Symbol::qualified("core", "Any"))
42        })
43        .cloned()
44        .unwrap_or(nil))
45}
46
47/// Resolve a general codec's encode-options shape: `core/EncodeOptions`, then
48/// `core/Any`, then nil. The shared fallback chain those codecs hand-rolled.
49pub fn resolve_options_shape(linker: &Linker) -> Result<ShapeRef> {
50    let nil = DefaultFactory.nil()?;
51    Ok(linker
52        .registry()
53        .shape_by_symbol(&Symbol::qualified("core", "EncodeOptions"))
54        .or_else(|| {
55            linker
56                .registry()
57                .shape_by_symbol(&Symbol::qualified("core", "Any"))
58        })
59        .cloned()
60        .unwrap_or(nil))
61}
62
63/// A host-registered lib that exports one codec (and optionally the Shapes it
64/// uses) built from a decoder and encoder.
65pub struct DomainCodecLib {
66    symbol: Symbol,
67    codec_id: CodecId,
68    decoder: Arc<dyn Decoder>,
69    encoder: Arc<dyn Encoder>,
70    expr_shape_symbol: Symbol,
71    shapes: Vec<(Symbol, ShapeRef)>,
72}
73
74impl DomainCodecLib {
75    /// Build a domain codec lib. `expr_shape_symbol` is the codec's expression
76    /// shape; it is resolved (at load) from the lib's own registered shapes,
77    /// then the registry, then `core/Expr`, then `core/Any`, then nil.
78    pub fn new(
79        symbol: Symbol,
80        codec_id: CodecId,
81        decoder: Arc<dyn Decoder>,
82        encoder: Arc<dyn Encoder>,
83        expr_shape_symbol: Symbol,
84    ) -> Self {
85        Self {
86            symbol,
87            codec_id,
88            decoder,
89            encoder,
90            expr_shape_symbol,
91            shapes: Vec::new(),
92        }
93    }
94
95    /// Also register these Shapes when the lib loads (for codecs like
96    /// `codec:scene` that own their domain's node Shapes).
97    pub fn with_shapes(mut self, shapes: Vec<(Symbol, ShapeRef)>) -> Self {
98        self.shapes = shapes;
99        self
100    }
101}
102
103impl Lib for DomainCodecLib {
104    fn manifest(&self) -> LibManifest {
105        let mut exports: Vec<Export> = self
106            .shapes
107            .iter()
108            .map(|(symbol, _)| Export::Shape {
109                symbol: symbol.clone(),
110                shape_id: None,
111            })
112            .collect();
113        exports.push(Export::Codec {
114            symbol: self.symbol.clone(),
115            codec_id: Some(self.codec_id),
116        });
117        LibManifest {
118            id: self.symbol.clone(),
119            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
120            abi: AbiVersion { major: 0, minor: 1 },
121            target: LibTarget::HostRegistered,
122            requires: Vec::<Dependency>::new(),
123            capabilities: Vec::new(),
124            exports,
125        }
126    }
127
128    fn load(&self, _cx: &mut LoadCx, linker: &mut Linker<'_>) -> Result<()> {
129        for (symbol, shape) in &self.shapes {
130            linker.shape_value(symbol.clone(), shape.clone())?;
131        }
132        let nil = DefaultFactory.nil()?;
133        let expr_shape = self
134            .shapes
135            .iter()
136            .find(|(symbol, _)| symbol == &self.expr_shape_symbol)
137            .map(|(_, shape)| shape.clone())
138            .or_else(|| {
139                linker
140                    .registry()
141                    .shape_by_symbol(&self.expr_shape_symbol)
142                    .cloned()
143            })
144            .or_else(|| {
145                linker
146                    .registry()
147                    .shape_by_symbol(&Symbol::qualified("core", "Expr"))
148                    .cloned()
149            })
150            .or_else(|| {
151                linker
152                    .registry()
153                    .shape_by_symbol(&Symbol::qualified("core", "Any"))
154                    .cloned()
155            })
156            .unwrap_or_else(|| nil.clone());
157        linker.codec_value(
158            self.symbol.clone(),
159            codec_value(CodecRuntime {
160                id: self.codec_id,
161                symbol: self.symbol.clone(),
162                decoder: Some(self.decoder.clone()),
163                located_decoder: None,
164                tree_decoder: None,
165                encoder: Some(self.encoder.clone()),
166                located_encoder: None,
167                tree_encoder: None,
168                expr_shape,
169                options_shape: nil,
170                default_decode: CodecDefaultDecode::Datum,
171            }),
172        )?;
173        Ok(())
174    }
175}