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}