Skip to main content

brink_runtime/story/
external.rs

1//! External-function-call protocol: [`ExternalResult`], [`ExternalFnHandler`],
2//! [`FallbackHandler`], and engine→ink function-evaluation outcomes
3//! ([`FunctionEval`]).
4
5use brink_format::Value;
6
7/// Result of an external function handler call.
8#[derive(Debug, Clone)]
9pub enum ExternalResult {
10    /// The handler resolved the call and returned a value.
11    /// `Value::Null` is valid for fire-and-forget calls.
12    Resolved(Value),
13    /// The handler declined — use the ink fallback body if available.
14    Fallback,
15    /// The handler cannot resolve the call yet (async resolution).
16    /// The VM freezes with the `External` frame intact. The caller must
17    /// resolve via `story.resolve_external(value)` before continuing.
18    Pending,
19}
20
21/// Trait for handling external function calls from ink.
22///
23/// Implement this to provide runtime-injected external function behavior.
24/// The orchestration layer calls [`call`](ExternalFnHandler::call) when the
25/// VM encounters a `CallExternal` opcode. The handler can resolve the call
26/// immediately, decline to handle it (triggering fallback), or in the future,
27/// indicate that resolution is pending (async/WASM).
28pub trait ExternalFnHandler {
29    /// Handle an external function call.
30    ///
31    /// `name` is the ink-declared function name. `args` are the values
32    /// popped from the value stack, in declaration order.
33    fn call(&self, name: &str, args: &[Value]) -> ExternalResult;
34}
35
36/// Default handler that always falls back to the ink function body.
37///
38/// Use this as the `handler` argument to [`FlowInstance::step_single_line`]
39/// or [`FlowInstance::choose`] when you don't want to provide a custom
40/// external-function binding registry. Every external call returns
41/// [`ExternalResult::Fallback`], delegating to the in-story fallback
42/// container declared on the `EXTERNAL` declaration.
43pub struct FallbackHandler;
44
45impl ExternalFnHandler for FallbackHandler {
46    fn call(&self, _name: &str, _args: &[Value]) -> ExternalResult {
47        ExternalResult::Fallback
48    }
49}
50
51/// Outcome of an engine→ink function evaluation
52/// ([`FlowInstance::begin_function_eval`] / [`resume_function_eval`](FlowInstance::resume_function_eval)).
53///
54/// Evaluating an ink function from engine code does not advance the
55/// player-visible story: its output is isolated and discarded, and the
56/// transcript is untouched. The only result is the function's return
57/// value — unless the function calls an external that can't be resolved
58/// synchronously.
59#[derive(Debug, Clone)]
60pub enum FunctionEval {
61    /// The function returned this value and evaluation is complete.
62    /// (Functions with no explicit `~ return` yield [`Value::Null`].)
63    Returned(Value),
64    /// The function called an external whose handler returned
65    /// [`ExternalResult::Pending`] — typically a binding that needs
66    /// engine/World access resolved out-of-band. Evaluation is paused
67    /// with its full state intact. Inspect the pending call via
68    /// [`pending_external_name`](FlowInstance::pending_external_name) /
69    /// [`pending_external_args`](FlowInstance::pending_external_args),
70    /// supply the result with
71    /// [`resolve_external`](FlowInstance::resolve_external), then call
72    /// [`resume_function_eval`](FlowInstance::resume_function_eval).
73    AwaitingExternal,
74}