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}