Skip to main content

sim_lib_standard_core/
guest_kit.rs

1//! Language-neutral runtime policy kit for guest profiles.
2
3use std::{fmt, sync::Arc};
4
5use sim_kernel::{Cx, Result, Value};
6
7/// How a guest language decides whether a runtime value is truthy.
8pub trait TruthPolicy: Send + Sync {
9    /// Return whether `value` counts as truthy for this guest profile.
10    fn is_truthy(&self, cx: &mut Cx, value: &Value) -> Result<bool>;
11}
12
13/// How a guest language coerces values at number and string boundaries.
14pub trait CoercionPolicy: Send + Sync {
15    /// Convert `value` to a number value when this profile accepts such a coercion.
16    fn to_number(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>>;
17
18    /// Convert `value` to a string value when this profile accepts such a coercion.
19    fn to_string(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>>;
20}
21
22/// Multivalue arity rule applied at a guest language boundary.
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum Arity {
25    /// Keep exactly this many values, padding with the profile nil value.
26    Exact(usize),
27    /// Keep one value, padding with the profile nil value when none were returned.
28    AtLeastOne,
29    /// Keep every returned value.
30    All,
31}
32
33/// Apply `rule` to returned values, using `nil` when padding is needed.
34pub fn adjust_values(mut values: Vec<Value>, rule: Arity, nil: Value) -> Vec<Value> {
35    match rule {
36        Arity::All => values,
37        Arity::AtLeastOne => {
38            if values.is_empty() {
39                vec![nil]
40            } else {
41                values.truncate(1);
42                values
43            }
44        }
45        Arity::Exact(count) => {
46            values.resize(count, nil);
47            values
48        }
49    }
50}
51
52/// Runtime policy bundle a language profile configures once and reuses.
53#[derive(Clone)]
54pub struct GuestRuntimeKit {
55    /// Truthiness policy for this profile.
56    pub truth: Arc<dyn TruthPolicy>,
57    /// Boundary coercion policy for this profile.
58    pub coerce: Arc<dyn CoercionPolicy>,
59    /// Profile-specific nil value used for arity padding.
60    pub nil: Value,
61}
62
63impl GuestRuntimeKit {
64    /// Build a kit from truthiness, coercion, and nil policies.
65    pub fn new(truth: Arc<dyn TruthPolicy>, coerce: Arc<dyn CoercionPolicy>, nil: Value) -> Self {
66        Self { truth, coerce, nil }
67    }
68
69    /// Return whether `value` counts as truthy for this profile.
70    pub fn is_truthy(&self, cx: &mut Cx, value: &Value) -> Result<bool> {
71        self.truth.is_truthy(cx, value)
72    }
73
74    /// Convert `value` to a number value when this profile accepts such a coercion.
75    pub fn to_number(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>> {
76        self.coerce.to_number(cx, value)
77    }
78
79    /// Convert `value` to a string value when this profile accepts such a coercion.
80    pub fn to_string(&self, cx: &mut Cx, value: &Value) -> Result<Option<Value>> {
81        self.coerce.to_string(cx, value)
82    }
83
84    /// Apply an arity rule using this profile's nil value for padding.
85    pub fn adjust_values(&self, values: Vec<Value>, rule: Arity) -> Vec<Value> {
86        adjust_values(values, rule, self.nil.clone())
87    }
88}
89
90impl fmt::Debug for GuestRuntimeKit {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        f.debug_struct("GuestRuntimeKit")
93            .field("nil", &self.nil)
94            .finish_non_exhaustive()
95    }
96}