Skip to main content

bubbles/runtime/
provider.rs

1//! [`LineProvider`] trait for localisation / text substitution.
2
3/// Supplies localised (or otherwise substituted) text for a line.
4///
5/// When a line carries a `#line:<id>` tag the runner queries the provider with
6/// that id **before** evaluating any `{expr}` placeholders (translate-then-format
7/// ordering).  The returned string is itself a template: any `{expr}` it
8/// contains are evaluated against the current variable storage, so translators
9/// can reorder or reshape interpolations however their language requires.
10///
11/// The same id is also exposed on [`crate::DialogueEvent::Line`] as `line_id`
12/// (see [`crate::line_id_from_tags`]).
13///
14/// If the provider returns `None`, the original source text (with its own
15/// `{expr}` segments) is used unchanged.
16pub trait LineProvider: Send + Sync + 'static {
17    /// Returns a localised template for `line_id`, or `None` to fall back to
18    /// the source text.
19    ///
20    /// The returned string may contain `{expr}` placeholders; they will be
21    /// evaluated after translation.
22    fn get(&self, line_id: &str) -> Option<String>;
23}
24
25/// A no-op provider that always returns `None` (source text is used unchanged).
26#[derive(Debug, Clone, Default)]
27pub struct PassthroughProvider;
28
29impl LineProvider for PassthroughProvider {
30    fn get(&self, _line_id: &str) -> Option<String> {
31        None
32    }
33}
34
35/// A simple in-memory provider backed by a [`std::collections::HashMap`].
36#[derive(Debug, Clone, Default)]
37pub struct HashMapProvider {
38    map: std::collections::HashMap<String, String>,
39}
40
41impl HashMapProvider {
42    /// Creates an empty provider.
43    #[must_use]
44    pub fn new() -> Self {
45        Self::default()
46    }
47
48    /// Inserts a localised string for the given id.
49    pub fn insert(&mut self, id: impl Into<String>, text: impl Into<String>) {
50        self.map.insert(id.into(), text.into());
51    }
52}
53
54impl LineProvider for HashMapProvider {
55    fn get(&self, line_id: &str) -> Option<String> {
56        self.map.get(line_id).cloned()
57    }
58}