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}