bubbles/runtime/builder.rs
1//! [`RunnerBuilder`] — ergonomic one-shot construction of a configured [`Runner`].
2
3use crate::compiler::Program;
4use crate::library::{FunctionLibrary, HostFn};
5use crate::runtime::provider::{LineProvider, PassthroughProvider};
6use crate::saliency::{FirstAvailable, SaliencyStrategy};
7use crate::value::VariableStorage;
8
9use super::Runner;
10
11/// Builds a [`Runner`] with optional saliency, provider, and host-function
12/// configuration in a single fluent chain.
13///
14/// Use this when you need to customise at least one of those at construction
15/// time; for the zero-config case [`Runner::new`] is still the fastest path.
16///
17/// # Example
18///
19/// ```rust
20/// use bubbles::{HashMapStorage, RunnerBuilder, Value, compile};
21/// use bubbles::saliency::BestLeastRecentlyViewed;
22///
23/// let prog = compile("title: A\n---\nHello.\n===\n").unwrap();
24/// let mut runner = RunnerBuilder::new(prog, HashMapStorage::new())
25/// .with_saliency(BestLeastRecentlyViewed::default())
26/// .with_function("greet", |_args| Ok(Value::Text("hi".into())))
27/// .build();
28/// runner.start("A").unwrap();
29/// ```
30pub struct RunnerBuilder<S: VariableStorage> {
31 program: Program,
32 storage: S,
33 saliency: Box<dyn SaliencyStrategy>,
34 provider: Box<dyn LineProvider>,
35 library: FunctionLibrary,
36}
37
38impl<S: VariableStorage> RunnerBuilder<S> {
39 /// Creates a builder with defaults matching [`Runner::new`]:
40 /// [`FirstAvailable`] saliency and [`PassthroughProvider`].
41 pub fn new(program: Program, storage: S) -> Self {
42 Self {
43 program,
44 storage,
45 saliency: Box::new(FirstAvailable),
46 provider: Box::new(PassthroughProvider),
47 library: FunctionLibrary::new(),
48 }
49 }
50
51 /// Overrides the saliency strategy used for line and node group selection.
52 #[must_use]
53 pub fn with_saliency(mut self, strategy: impl SaliencyStrategy) -> Self {
54 self.saliency = Box::new(strategy);
55 self
56 }
57
58 /// Overrides the line provider used for localisation lookup.
59 #[must_use]
60 pub fn with_provider(mut self, provider: impl LineProvider) -> Self {
61 self.provider = Box::new(provider);
62 self
63 }
64
65 /// Registers a host function available to dialogue expressions.
66 ///
67 /// This is a convenience wrapper around
68 /// [`FunctionLibrary::register`] that keeps configuration on the builder
69 /// rather than requiring a post-construction `library_mut()` call.
70 #[must_use]
71 pub fn with_function<F>(mut self, name: &str, f: F) -> Self
72 where
73 F: Fn(Vec<crate::value::Value>) -> crate::error::Result<crate::value::Value>
74 + Send
75 + Sync
76 + 'static,
77 {
78 self.library.register(name, Box::new(f) as HostFn);
79 self
80 }
81
82 /// Consumes the builder and returns a fully configured [`Runner`].
83 ///
84 /// The runner is in the `Idle` state; call [`Runner::start`] to begin
85 /// execution.
86 #[must_use]
87 pub fn build(self) -> Runner<S> {
88 let mut runner = Runner::new(self.program, self.storage);
89 runner.set_saliency_box(self.saliency);
90 runner.set_provider_box(self.provider);
91 *runner.library_mut() = self.library;
92 runner
93 }
94}