Skip to main content

vihaco_runtime/
build_message.rs

1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4use crate::{Execute, Field, NoMessage};
5
6/// Build execution input for one composite field route and instruction type.
7///
8/// Available with the experimental `typed-declarations` feature. The selected
9/// child's [`Execute<I>`] implementation determines the message type. The `M`
10/// parameter defaults to that associated type. `Context` defaults to `()`;
11/// runners can instead supply immutable runtime data such as a borrowed module.
12/// Consumers can write `C: BuildMessage<F, I, Context>`.
13///
14/// Standard message types supply framework preparation automatically, including
15/// [`NoMessage`] and typed stack inputs. Custom message types can have explicit
16/// author-provided builders; authors do not implement the internal preparation
17/// hook used by standard messages.
18///
19/// Specify `M` explicitly in those implementations, such as
20/// `BuildMessage<F, Call, (), FunctionInfo>`. This keeps their trait identities
21/// disjoint from standard message preparation without relying on cross-crate
22/// associated-type normalization during coherence checking. The equality bound
23/// still requires `M` to match the child's execution message; it cannot select
24/// a different one.
25///
26/// A [`crate::Step`] implementation can use this optional helper to separate
27/// message construction from execution and effect handling. Implementing this
28/// trait neither executes an instruction nor supplies a `Step` implementation.
29///
30/// ```
31/// use vihaco_runtime::{BuildMessage, Execute, Field};
32///
33/// fn prepare<C, F, I>(
34///     composite: &mut C,
35///     instruction: &I,
36/// ) -> vihaco_runtime::eyre::Result<<C::Child as Execute<I>>::Message>
37/// where
38///     C: BuildMessage<F, I>,
39///     C::Child: Execute<I>,
40/// {
41///     composite.build_message(instruction, &())
42/// }
43/// ```
44pub trait BuildMessage<
45    F,
46    I,
47    Context: ?Sized = (),
48    M = <<Self as Field<F>>::Child as Execute<I>>::Message,
49>: Field<F> where
50    Self::Child: Execute<I, Message = M>,
51{
52    /// Resolve and validate the input needed to execute `instruction`.
53    ///
54    /// The builder may mutate the composite, for example by consuming stack
55    /// operands. Mutations made before an error are retained.
56    ///
57    /// # Errors
58    ///
59    /// Returns message-preparation failures, such as invalid program references
60    /// or missing runtime inputs. Callers can propagate the original report
61    /// through [`crate::Step`] before invoking the child.
62    fn build_message(&mut self, instruction: &I, context: &Context) -> eyre::Result<M>;
63}
64
65/// Internal framework message-preparation hook. Ordinary component authors only
66/// select message types. This permits stdlib-owned messages to supply preparation
67/// without an orphan impl on every downstream composite.
68#[doc(hidden)]
69pub trait PrepareMessage<Parent, Route, I, Context: ?Sized>: Sized {
70    fn prepare(parent: &mut Parent, instruction: &I, context: &Context) -> eyre::Result<Self>;
71}
72
73/// Internal eligibility marker for framework-provided message preparation.
74///
75/// Keeping this marker free of instruction parameters lets Rust distinguish
76/// custom messages from automatic preparation when checking generic builders.
77#[doc(hidden)]
78pub trait AutomaticMessage {}
79
80impl AutomaticMessage for NoMessage {}
81
82#[cfg(feature = "program-image-execution")]
83impl<Values> AutomaticMessage for crate::PopFromStack<Values> {}
84
85impl<C, F, I, Context: ?Sized, M> BuildMessage<F, I, Context, M> for C
86where
87    C: Field<F>,
88    C::Child: Execute<I, Message = M>,
89    M: AutomaticMessage + PrepareMessage<C, F, I, Context>,
90{
91    #[inline(always)]
92    fn build_message(&mut self, instruction: &I, context: &Context) -> eyre::Result<M> {
93        M::prepare(self, instruction, context)
94    }
95}
96
97impl<C, F, I, Context: ?Sized> PrepareMessage<C, F, I, Context> for NoMessage
98where
99    C: Field<F>,
100    C::Child: Execute<I, Message = NoMessage>,
101{
102    #[inline]
103    fn prepare(_: &mut C, _: &I, _: &Context) -> eyre::Result<Self> {
104        Ok(NoMessage::new())
105    }
106}