Skip to main content

sim_lib_standard_core/
read_construct.rs

1//! Read/construct support exposing profiles and badges as runtime objects.
2
3use std::sync::Arc;
4
5use sim_kernel::{
6    AbiVersion, Args, CORE_CLASS_CLASS_ID, CORE_FUNCTION_CLASS_ID, Callable, Class, ClassId,
7    ClassRef, Cx, DefaultFactory, Export, Expr, Factory, Lib, LibManifest, LibTarget, Linker,
8    Object, ObjectCompat, ObjectEncode, ObjectEncoding, ReadConstructor, ReadConstructorRef, Ref,
9    Result, ShapeRef, Symbol, TableRef, Value, Version,
10};
11
12use crate::{
13    FidelityBadge, LanguageProfile, fidelity_badge_class_symbol, language_profile_class_symbol,
14};
15
16const PROFILE_CLASS_ID: ClassId = ClassId(6100);
17const FIDELITY_BADGE_CLASS_ID: ClassId = ClassId(6101);
18
19/// Runtime object wrapping a [`LanguageProfile`] as the `standard/Profile` class.
20#[derive(Clone)]
21pub struct LanguageProfileValue {
22    profile: LanguageProfile,
23}
24
25impl LanguageProfileValue {
26    /// Wrap `profile` as a runtime value.
27    pub fn new(profile: LanguageProfile) -> Self {
28        Self { profile }
29    }
30
31    /// The wrapped profile.
32    pub fn profile(&self) -> &LanguageProfile {
33        &self.profile
34    }
35}
36
37impl Object for LanguageProfileValue {
38    fn display(&self, _cx: &mut Cx) -> Result<String> {
39        Ok(format!("#<standard-profile {}>", self.profile.symbol))
40    }
41
42    fn as_any(&self) -> &dyn std::any::Any {
43        self
44    }
45}
46
47impl ObjectCompat for LanguageProfileValue {
48    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
49        class_value_or_stub(cx, PROFILE_CLASS_ID, language_profile_class_symbol())
50    }
51
52    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
53        Ok(Expr::Call {
54            operator: Box::new(Expr::Symbol(language_profile_class_symbol())),
55            args: self.profile.to_constructor_args(),
56        })
57    }
58
59    fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
60        Some(self)
61    }
62}
63
64impl ObjectEncode for LanguageProfileValue {
65    fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
66        Ok(ObjectEncoding::Constructor {
67            class: language_profile_class_symbol(),
68            args: self.profile.to_constructor_args(),
69        })
70    }
71}
72
73impl sim_citizen::Citizen for LanguageProfileValue {
74    fn citizen_symbol() -> Symbol {
75        language_profile_class_symbol()
76    }
77
78    fn citizen_version() -> u32 {
79        0
80    }
81
82    fn citizen_arity() -> usize {
83        11
84    }
85
86    fn citizen_fields() -> &'static [&'static str] {
87        &[
88            "symbol",
89            "reader",
90            "lowering",
91            "eval_policy",
92            "organs",
93            "backing_requirements",
94            "numeric_tower",
95            "capabilities",
96            "unsupported_forms",
97            "conformance_tests",
98            "fidelity_badges",
99        ]
100    }
101}
102
103/// Runtime object wrapping a [`FidelityBadge`] as the `standard/FidelityBadge` class.
104#[derive(Clone)]
105pub struct FidelityBadgeValue {
106    badge: FidelityBadge,
107}
108
109impl FidelityBadgeValue {
110    /// Wrap `badge` as a runtime value.
111    pub fn new(badge: FidelityBadge) -> Self {
112        Self { badge }
113    }
114
115    /// The wrapped badge.
116    pub fn badge(&self) -> &FidelityBadge {
117        &self.badge
118    }
119}
120
121impl Object for FidelityBadgeValue {
122    fn display(&self, _cx: &mut Cx) -> Result<String> {
123        Ok(format!("#<standard-fidelity {}>", self.badge.badge))
124    }
125
126    fn as_any(&self) -> &dyn std::any::Any {
127        self
128    }
129}
130
131impl ObjectCompat for FidelityBadgeValue {
132    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
133        class_value_or_stub(cx, FIDELITY_BADGE_CLASS_ID, fidelity_badge_class_symbol())
134    }
135
136    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
137        Ok(Expr::Call {
138            operator: Box::new(Expr::Symbol(fidelity_badge_class_symbol())),
139            args: self.badge.to_constructor_args(),
140        })
141    }
142
143    fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
144        Some(self)
145    }
146}
147
148impl ObjectEncode for FidelityBadgeValue {
149    fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
150        Ok(ObjectEncoding::Constructor {
151            class: fidelity_badge_class_symbol(),
152            args: self.badge.to_constructor_args(),
153        })
154    }
155}
156
157impl sim_citizen::Citizen for FidelityBadgeValue {
158    fn citizen_symbol() -> Symbol {
159        fidelity_badge_class_symbol()
160    }
161
162    fn citizen_version() -> u32 {
163        0
164    }
165
166    fn citizen_arity() -> usize {
167        4
168    }
169
170    fn citizen_fields() -> &'static [&'static str] {
171        &["subject", "badge", "level", "evidence"]
172    }
173}
174
175/// Register the `standard/Profile` and `standard/FidelityBadge` classes into the
176/// registry, idempotently.
177///
178/// This installs the read/construct surface that lets the standard distribution
179/// round-trip profiles and badges as runtime objects; classes already present
180/// are left untouched.
181pub fn install_standard_core_classes(cx: &mut Cx) -> Result<()> {
182    sim_lib_core::install_once(cx, &StandardCoreClassesLib).map(|_| ())
183}
184
185/// Manifest id for the standard read/construct class lib.
186pub fn standard_core_classes_lib_symbol() -> Symbol {
187    Symbol::qualified("standard", "classes")
188}
189
190struct StandardCoreClassesLib;
191
192impl Lib for StandardCoreClassesLib {
193    fn manifest(&self) -> LibManifest {
194        LibManifest {
195            id: standard_core_classes_lib_symbol(),
196            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
197            abi: AbiVersion { major: 0, minor: 1 },
198            target: LibTarget::HostRegistered,
199            requires: Vec::new(),
200            capabilities: Vec::new(),
201            exports: [StandardClassKind::Profile, StandardClassKind::FidelityBadge]
202                .into_iter()
203                .map(|kind| Export::Class {
204                    symbol: kind.symbol(),
205                    class_id: Some(kind.id()),
206                })
207                .collect(),
208        }
209    }
210
211    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
212        register_standard_class(linker, StandardClassKind::Profile)?;
213        register_standard_class(linker, StandardClassKind::FidelityBadge)
214    }
215}
216
217#[derive(Clone, Copy)]
218enum StandardClassKind {
219    Profile,
220    FidelityBadge,
221}
222
223impl StandardClassKind {
224    fn id(self) -> ClassId {
225        match self {
226            Self::Profile => PROFILE_CLASS_ID,
227            Self::FidelityBadge => FIDELITY_BADGE_CLASS_ID,
228        }
229    }
230
231    fn symbol(self) -> Symbol {
232        match self {
233            Self::Profile => language_profile_class_symbol(),
234            Self::FidelityBadge => fidelity_badge_class_symbol(),
235        }
236    }
237
238    fn display_name(self) -> &'static str {
239        match self {
240            Self::Profile => "standard/Profile",
241            Self::FidelityBadge => "standard/FidelityBadge",
242        }
243    }
244}
245
246#[derive(Clone)]
247struct StandardClass {
248    kind: StandardClassKind,
249}
250
251impl Object for StandardClass {
252    fn display(&self, _cx: &mut Cx) -> Result<String> {
253        Ok(format!("#<class {}>", self.kind.display_name()))
254    }
255
256    fn as_any(&self) -> &dyn std::any::Any {
257        self
258    }
259}
260
261impl ObjectCompat for StandardClass {
262    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
263        class_value_or_stub(cx, CORE_CLASS_CLASS_ID, Symbol::qualified("core", "Class"))
264    }
265
266    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
267        Ok(Expr::Symbol(self.kind.symbol()))
268    }
269
270    fn as_callable(&self) -> Option<&dyn Callable> {
271        Some(self)
272    }
273
274    fn as_class(&self) -> Option<&dyn Class> {
275        Some(self)
276    }
277}
278
279impl Callable for StandardClass {
280    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
281        construct_standard_value(cx, self.kind, args.into_vec())
282    }
283}
284
285impl Class for StandardClass {
286    fn id(&self) -> ClassId {
287        self.kind.id()
288    }
289
290    fn symbol(&self) -> Symbol {
291        self.kind.symbol()
292    }
293
294    fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
295        cx.factory().nil()
296    }
297
298    fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
299        cx.factory().nil()
300    }
301
302    fn read_constructor(&self, _cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
303        Ok(Some(DefaultFactory.opaque(Arc::new(
304            StandardReadConstructor { kind: self.kind },
305        ))?))
306    }
307
308    fn members(&self, cx: &mut Cx) -> Result<TableRef> {
309        cx.factory().table(Vec::new())
310    }
311}
312
313#[derive(Clone)]
314struct StandardReadConstructor {
315    kind: StandardClassKind,
316}
317
318impl Object for StandardReadConstructor {
319    fn display(&self, _cx: &mut Cx) -> Result<String> {
320        Ok(format!("#<read-constructor {}>", self.kind.display_name()))
321    }
322
323    fn as_any(&self) -> &dyn std::any::Any {
324        self
325    }
326}
327
328impl ObjectCompat for StandardReadConstructor {
329    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
330        class_value_or_stub(
331            cx,
332            CORE_FUNCTION_CLASS_ID,
333            Symbol::qualified("core", "Function"),
334        )
335    }
336
337    fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
338        Some(self)
339    }
340}
341
342impl ReadConstructor for StandardReadConstructor {
343    fn symbol(&self) -> Symbol {
344        self.kind.symbol()
345    }
346
347    fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
348        cx.factory().nil()
349    }
350
351    fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
352        construct_standard_value(cx, self.kind, args)
353    }
354}
355
356fn construct_standard_value(
357    cx: &mut Cx,
358    kind: StandardClassKind,
359    args: Vec<Value>,
360) -> Result<Value> {
361    let exprs = value_exprs(cx, args)?;
362    match kind {
363        StandardClassKind::Profile => cx.factory().opaque(Arc::new(LanguageProfileValue::new(
364            LanguageProfile::from_constructor_args(exprs)?,
365        ))),
366        StandardClassKind::FidelityBadge => cx.factory().opaque(Arc::new(FidelityBadgeValue::new(
367            FidelityBadge::from_constructor_args(exprs)?,
368        ))),
369    }
370}
371
372fn value_exprs(cx: &mut Cx, args: Vec<Value>) -> Result<Vec<Expr>> {
373    let mut exprs = Vec::with_capacity(args.len());
374    for value in args {
375        exprs.push(value.object().as_expr(cx)?);
376    }
377    Ok(exprs)
378}
379
380fn register_standard_class(linker: &mut Linker<'_>, kind: StandardClassKind) -> Result<()> {
381    let class = DefaultFactory
382        .opaque(Arc::new(StandardClass { kind }))
383        .expect("standard class should be boxable");
384    linker.class_value(kind.symbol(), class)?;
385    Ok(())
386}
387
388fn install_language_profile_citizen(linker: &mut Linker<'_>) -> Result<()> {
389    register_standard_class(linker, StandardClassKind::Profile)
390}
391
392fn install_fidelity_badge_citizen(linker: &mut Linker<'_>) -> Result<()> {
393    register_standard_class(linker, StandardClassKind::FidelityBadge)
394}
395
396fn conformance_language_profile_citizen(cx: &mut Cx) -> Result<()> {
397    let profile = LanguageProfile::new(Symbol::qualified("standard-citizen", "profile"));
398    let value = cx
399        .factory()
400        .opaque(Arc::new(LanguageProfileValue::new(profile)))?;
401    sim_citizen::check_value_fixture(cx, value)
402}
403
404fn conformance_fidelity_badge_citizen(cx: &mut Cx) -> Result<()> {
405    let badge = FidelityBadge::new(
406        Ref::Symbol(Symbol::qualified("standard-citizen", "subject")),
407        Symbol::qualified("standard-citizen", "badge"),
408        2,
409        Ref::Symbol(Symbol::qualified("standard-citizen", "evidence")),
410    );
411    let value = cx
412        .factory()
413        .opaque(Arc::new(FidelityBadgeValue::new(badge)))?;
414    sim_citizen::check_value_fixture(cx, value)
415}
416
417sim_citizen::inventory::submit! {
418    sim_citizen::CitizenInfo {
419        symbol: "standard/Profile",
420        version: 0,
421        crate_name: env!("CARGO_PKG_NAME"),
422        arity: 10,
423        install: install_language_profile_citizen,
424        conformance: conformance_language_profile_citizen,
425    }
426}
427
428sim_citizen::inventory::submit! {
429    sim_citizen::CitizenInfo {
430        symbol: "standard/FidelityBadge",
431        version: 0,
432        crate_name: env!("CARGO_PKG_NAME"),
433        arity: 4,
434        install: install_fidelity_badge_citizen,
435        conformance: conformance_fidelity_badge_citizen,
436    }
437}
438
439fn class_value_or_stub(cx: &mut Cx, id: ClassId, symbol: Symbol) -> Result<Value> {
440    if let Some(value) = cx.registry().class_by_symbol(&symbol) {
441        return Ok(value.clone());
442    }
443    cx.factory().class_stub(id, symbol)
444}