hephae_locale/
cmd.rs

1//! Defines extensions for [`Commands`] and [`EntityCommands`] for convenient localized entity
2//! spawning.
3
4use std::borrow::Cow;
5
6use bevy_asset::prelude::*;
7use bevy_ecs::prelude::*;
8use bevy_utils::all_tuples;
9use smallvec::{SmallVec, smallvec};
10
11use crate::{
12    arg::LocaleArg,
13    def::{LocaleArgs, LocaleCache, LocaleCollection, LocaleKey, LocaleSrc},
14};
15
16/// A set of [`LocaleArg`] objects, implemented for tuples.
17pub trait LocBundle {
18    #[doc(hidden)]
19    fn spawn(this: Self, commands: Commands) -> SmallVec<[Entity; 4]>;
20}
21
22macro_rules! impl_loc_bundle {
23    ((T0, t0)) => {
24        #[cfg_attr(docsrs, doc(fake_variadic))]
25        impl<T0: LocBundle> LocBundle for (T0,) {
26            #[allow(unused)]
27            fn spawn((t0,): Self, mut commands: Commands) -> SmallVec<[Entity; 4]> {
28                T0::spawn(t0, commands.reborrow())
29            }
30        }
31    };
32    ($(($T:ident, $t:ident)),*) => {
33        #[doc(hidden)]
34        impl<$($T: LocBundle),*> LocBundle for ($($T,)*) {
35            #[allow(unused)]
36            fn spawn(($($t,)*): Self, mut commands: Commands) -> SmallVec<[Entity; 4]> {
37                let mut out = SmallVec::new();
38                $(out.append(&mut $T::spawn($t, commands.reborrow()));)*
39                out
40            }
41        }
42    };
43}
44
45all_tuples!(impl_loc_bundle, 0, 15, T, t);
46
47impl<T: LocaleArg> LocBundle for T {
48    #[inline]
49    fn spawn(this: Self, mut commands: Commands) -> SmallVec<[Entity; 4]> {
50        smallvec![
51            commands
52                .spawn((LocaleSrc(this), LocaleCache {
53                    result: None,
54                    locale: AssetId::default(),
55                    changed: false,
56                }))
57                .id()
58        ]
59    }
60}
61
62/// Extension for [`Commands`], allowing users to efficiently spawn entities with localization
63/// support.
64pub trait LocCommandsExt {
65    /// [`Commands::spawn`], but also inserts necessary localization components.
66    fn spawn_localized<L: LocBundle>(
67        &mut self,
68        bundle: impl Bundle,
69        key: impl Into<Cow<'static, str>>,
70        handle: Handle<LocaleCollection>,
71        loc: L,
72    ) -> EntityCommands;
73
74    /// [`Commands::spawn_empty`], but also inserts necessary localization components.
75    fn spawn_localized_empty<L: LocBundle>(
76        &mut self,
77        key: impl Into<Cow<'static, str>>,
78        handle: Handle<LocaleCollection>,
79        loc: L,
80    ) -> EntityCommands;
81}
82
83impl LocCommandsExt for Commands<'_, '_> {
84    #[inline]
85    fn spawn_localized<L: LocBundle>(
86        &mut self,
87        bundle: impl Bundle,
88        key: impl Into<Cow<'static, str>>,
89        handle: Handle<LocaleCollection>,
90        loc: L,
91    ) -> EntityCommands {
92        let args = L::spawn(loc, self.reborrow());
93        self.spawn((
94            bundle,
95            LocaleKey {
96                key: key.into(),
97                collection: handle,
98            },
99            LocaleArgs(args),
100        ))
101    }
102
103    #[inline]
104    fn spawn_localized_empty<L: LocBundle>(
105        &mut self,
106        key: impl Into<Cow<'static, str>>,
107        handle: Handle<LocaleCollection>,
108        loc: L,
109    ) -> EntityCommands {
110        let args = L::spawn(loc, self.reborrow());
111        self.spawn((
112            LocaleKey {
113                key: key.into(),
114                collection: handle,
115            },
116            LocaleArgs(args),
117        ))
118    }
119}
120
121/// Extension for [`EntityCommands`] and [`EntityWorldMut`], allowing users to localize existing
122/// entities.
123pub trait LocEntityExt {
124    /// Inserts necessary localization components.
125    fn localize<L: LocBundle>(
126        &mut self,
127        key: impl Into<Cow<'static, str>>,
128        handle: Handle<LocaleCollection>,
129        loc: L,
130    ) -> &mut Self;
131}
132
133impl LocEntityExt for EntityCommands<'_> {
134    #[inline]
135    fn localize<L: LocBundle>(
136        &mut self,
137        key: impl Into<Cow<'static, str>>,
138        handle: Handle<LocaleCollection>,
139        loc: L,
140    ) -> &mut Self {
141        let args = L::spawn(loc, self.commands());
142        self.insert((
143            LocaleKey {
144                key: key.into(),
145                collection: handle,
146            },
147            LocaleArgs(args),
148        ))
149    }
150}
151
152impl LocEntityExt for EntityWorldMut<'_> {
153    #[inline]
154    fn localize<L: LocBundle>(
155        &mut self,
156        key: impl Into<Cow<'static, str>>,
157        handle: Handle<LocaleCollection>,
158        loc: L,
159    ) -> &mut Self {
160        let bundle = self.world_scope(|world| {
161            let cmd = world.commands();
162            let args = L::spawn(loc, cmd);
163
164            (
165                LocaleKey {
166                    key: key.into(),
167                    collection: handle,
168                },
169                LocaleArgs(args),
170            )
171        });
172
173        self.insert(bundle);
174        self.world_scope(World::flush);
175        self
176    }
177}