Skip to main content

sim_lib_view/
render.rs

1//! Lens render and propose helpers.
2//!
3//! These tie the contracts to the scene and intent domains: a view's output
4//! must validate as a Scene, and an editor only sees an Intent that has already
5//! passed `codec:intent` validation. Both fail closed: an invalid scene or
6//! intent is a diagnostic, never a silent partial result.
7
8use sim_kernel::{Cx, Error, Expr, Result, Symbol};
9
10use crate::contract::{Draft, Operation};
11use crate::dispatch::LensRegistry;
12
13impl LensRegistry {
14    /// Render `value` through the named view lens, validating the emitted Scene.
15    pub fn render(&self, cx: &mut Cx, lens_id: &Symbol, value: &Expr) -> Result<Expr> {
16        let lens = self
17            .get(lens_id)
18            .ok_or_else(|| Error::HostError(format!("unknown lens {lens_id}")))?;
19        let view = lens
20            .view
21            .as_ref()
22            .ok_or_else(|| Error::HostError(format!("lens {lens_id} is not a view")))?;
23        let scene = view.encode(cx, value)?;
24        sim_lib_scene::validate_scene(&scene).map_err(|error| {
25            Error::HostError(format!("view {lens_id} produced an invalid scene: {error}"))
26        })?;
27        Ok(scene)
28    }
29
30    /// Fold an Intent into a draft through the named editor lens, after the
31    /// Intent passes structural validation.
32    pub fn propose(
33        &self,
34        cx: &mut Cx,
35        lens_id: &Symbol,
36        value: &Expr,
37        intent: &Expr,
38    ) -> Result<Draft> {
39        sim_lib_intent::validate_intent(intent)
40            .map_err(|error| Error::HostError(format!("invalid intent: {error}")))?;
41        let editor = self.editor_of(lens_id)?;
42        editor.decode(cx, value, intent)
43    }
44
45    /// Commit a committable draft through the named editor lens.
46    pub fn commit(&self, cx: &mut Cx, lens_id: &Symbol, draft: &Draft) -> Result<Operation> {
47        if !draft.committable {
48            return Err(Error::HostError(format!(
49                "draft is not committable ({} diagnostic(s))",
50                draft.diagnostics.len()
51            )));
52        }
53        self.editor_of(lens_id)?.commit(cx, draft)
54    }
55
56    fn editor_of(&self, lens_id: &Symbol) -> Result<std::sync::Arc<dyn crate::contract::Editor>> {
57        let lens = self
58            .get(lens_id)
59            .ok_or_else(|| Error::HostError(format!("unknown lens {lens_id}")))?;
60        lens.editor
61            .clone()
62            .ok_or_else(|| Error::HostError(format!("lens {lens_id} is not an editor")))
63    }
64}