Skip to main content

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}