rusty18n/
core.rs

1#[cfg(feature = "bevy_reflect")]
2use bevy_reflect::Reflect;
3use std::{collections::HashMap, hash::Hash};
4
5#[cfg(feature = "bevy_reflect")]
6fn default_dynamic_resource<A>() -> fn(A) -> String {
7    |_args: A| String::new()
8}
9
10/// A struct representing an internationalization (i18n) dynamic resource.
11#[derive(Debug)]
12#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
13pub struct I18NDynamicResource<A> {
14    #[cfg_attr(
15        feature = "bevy_reflect",
16        reflect(ignore, default = "default_dynamic_resource")
17    )]
18    /// A function that takes arguments of type `A` and returns a string representing
19    /// the localized resource.
20    caller: fn(A) -> String,
21}
22
23impl<A> I18NDynamicResource<A> {
24    pub fn new(caller: fn(A) -> String) -> Self {
25        Self { caller }
26    }
27
28    /// Invokes the caller function with the provided arguments and returns the resulting string.
29    ///
30    /// # Arguments
31    /// * `args` - Arguments of type `A` to be passed to the caller function.
32    ///
33    /// # Returns
34    /// A string representing the localized resource.
35    pub fn with(&self, args: A) -> String {
36        (self.caller)(args)
37    }
38}
39
40/// A trait for defining fallback behavior in internationalization (i18n).
41/// It should be used when defining the main i18n component, it will be used
42/// when a given i18n resource tries to be acquired but isn't present for the
43/// given locale at that moment.
44pub trait I18NFallback {
45    fn fallback() -> Self;
46}
47
48/// This trait groups Key, Value types for a given I18N implementation.
49pub trait I18NTrait {
50    type K: Eq + Hash + Default + Copy;
51    type V: I18NFallback;
52}
53
54/// The I18NStore wraps a HashMap that maps key value pairs of Locale keys and localized
55/// implementations.
56#[derive(Debug)]
57pub struct I18NStore<L: I18NTrait>(pub HashMap<L::K, L::V>);
58
59impl<L: I18NTrait, F: Fn() -> L::V> From<Vec<(L::K, F)>> for I18NStore<L> {
60    fn from(value: Vec<(L::K, F)>) -> Self {
61        Self(value.into_iter().map(|(k, v)| (k, v())).collect())
62    }
63}
64
65/// A struct representing access to i18n resources, with fallback support.
66///
67/// This struct holds references to both the fallback and target i18n resources.
68/// It allows accessing resources by applying a provided accessor function.
69pub struct I18NAccess<'a, L: I18NTrait> {
70    pub fallback: &'a L::V,
71    pub to: &'a L::V,
72}
73
74impl<L: I18NTrait> I18NAccess<'_, L> {
75    /// Acquires a resource by applying the provided accessor function.
76    ///
77    /// This method attempts to access the target resource first and falls back to
78    /// the fallback resource if the target resource is not available.
79    ///
80    /// # Arguments
81    /// * `accessing` - A function that takes a reference to an i18n value and returns
82    ///                 an optional reference to the desired resource.
83    ///
84    /// # Returns
85    /// A reference to the acquired resource.
86    pub fn acquire<R>(&self, accessing: fn(&L::V) -> Option<&R>) -> &R {
87        accessing(self.to).unwrap_or_else(|| accessing(self.fallback).unwrap())
88    }
89}
90
91/// A wrapper for i18n resources, providing access and fallback support.
92#[derive(Debug)]
93pub struct I18NWrapper<K: Eq + Hash + Default + Copy, V: I18NFallback> {
94    pub store: I18NStore<Self>,
95    fallback: V,
96}
97
98impl<K: Eq + Hash + Default + Copy, V: I18NFallback> I18NTrait for I18NWrapper<K, V> {
99    type K = K;
100    type V = V;
101}
102
103impl<K: Eq + Hash + Default + Copy, V: I18NFallback> I18NWrapper<K, V>
104where
105    Self: I18NTrait<K = K, V = V>,
106{
107    /// Constructs a new `I18NWrapper` with the provided initial i18n resource store.
108    ///
109    /// # Arguments
110    /// * `store` - A vector of key-value pairs representing the initial i18n resource store.
111    ///
112    /// # Returns
113    /// A new `I18NWrapper` instance.
114    pub fn new(store: Vec<(K, fn() -> V)>) -> Self {
115        let mut store = I18NStore::from(store);
116
117        store.0.insert(K::default(), V::fallback());
118
119        Self {
120            store,
121            fallback: V::fallback(),
122        }
123    }
124
125    /// Gets a reference to the default i18n resource.
126    fn ref_default(&self) -> &V {
127        &self.fallback
128    }
129
130    /// Gets a reference to the i18n resource for the specified locale, if available.
131    ///
132    /// # Arguments
133    /// * `locale` - The locale for which to retrieve the i18n resource.
134    ///
135    /// # Returns
136    /// An optional reference to the i18n resource.
137    fn ref_opt(&self, locale: K) -> Option<&V> {
138        self.store.0.get(&locale)
139    }
140
141    /// Gets a reference to the i18n resource for the specified locale or falls back to the default.
142    ///
143    /// # Arguments
144    /// * `locale` - The locale for which to retrieve the i18n resource.
145    ///
146    /// # Returns
147    /// A reference to the i18n resource.
148    fn ref_any(&self, locale: K) -> &V {
149        self.ref_opt(locale).unwrap_or_else(|| self.ref_default())
150    }
151
152    /// Gets an access object for the specified locale.
153    ///
154    /// # Arguments
155    /// * `locale` - The locale for which to retrieve the i18n resource.
156    ///
157    /// # Returns
158    /// An `I18NAccess` object providing access to the i18n resource for the specified locale.
159    pub fn get(&self, locale: K) -> I18NAccess<Self> {
160        I18NAccess {
161            fallback: self.ref_default(),
162            to: self.ref_any(locale),
163        }
164    }
165}