Skip to main content

tilo_app/framework/
delivery.rs

1//! Delivering an admitted invocation to a widget without a host, for a test
2//! that drives a widget directly: handing a control's invocation back, or an
3//! action a published tree encoded. Apps answer their own operations as a
4//! typed effect vocabulary; this reaches only the toolkit's widgets inside
5//! them, the way the host does once it admitted an invocation.
6
7use ui_core::automerge::{AutoCommit, ObjId};
8use ui_core::host_operation::{ResourceOperation, ResourceOperationStart};
9use ui_core::widget::Widget;
10
11use super::Effected;
12
13/// The operation type a toolkit control's invocation travels under.
14const CONTROL_INVOKE: &str = "tilo.ui.control.invoke";
15
16/// Delivering an admitted invocation to a widget, as its host does. Answers
17/// how the widget started it — done at once with its output, or pending to
18/// finish later — or `None` when nothing in the widget answers it.
19pub trait HostDelivery {
20    /// Start the encoded operation (`type_id`, `payload`) of `kind`.
21    fn start_encoded(
22        &mut self,
23        kind: tilo_actions::OperationKind,
24        type_id: &str,
25        payload: &[u8],
26        doc: &mut AutoCommit,
27        path: &ObjId,
28    ) -> Result<Option<Effected>, String>;
29
30    /// Hand back the invocation of the toolkit control `control` (its id,
31    /// as its `tilo.control:` resource names it).
32    fn deliver_control(
33        &mut self,
34        kind: tilo_actions::OperationKind,
35        control: &str,
36        doc: &mut AutoCommit,
37        path: &ObjId,
38    ) -> Result<Option<Effected>, String> {
39        let payload = serde_json::to_vec(control).map_err(|error| error.to_string())?;
40        self.start_encoded(kind, CONTROL_INVOKE, &payload, doc, path)
41    }
42}
43
44impl<W: Widget + ?Sized> HostDelivery for W {
45    fn start_encoded(
46        &mut self,
47        kind: tilo_actions::OperationKind,
48        type_id: &str,
49        payload: &[u8],
50        doc: &mut AutoCommit,
51        path: &ObjId,
52    ) -> Result<Option<Effected>, String> {
53        let started = ui_core::host_operation::start_operation(
54            self,
55            &ResourceOperation::local(tilo_actions::InvocationId::new(), kind, type_id, payload),
56            &mut ui_core::widget::Edits::lend(doc),
57            path,
58        )?;
59        Ok(started.map(|started| match started {
60            ResourceOperationStart::Completed(output) => Effected::Done(output),
61            ResourceOperationStart::Accepted => Effected::Pending,
62        }))
63    }
64}