Skip to content

Commit 5b09a59

Browse files
Implement unzip
Signed-off-by: Luca Della Vedova <[email protected]>
1 parent 507573b commit 5b09a59

File tree

1 file changed

+86
-190
lines changed

1 file changed

+86
-190
lines changed

src/chain/unzip.rs

+86-190
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*/
1717

18-
use bevy::prelude::{Entity, Commands};
18+
use bevy::utils::all_tuples;
1919

2020
use smallvec::SmallVec;
2121

@@ -29,174 +29,82 @@ pub trait Unzippable: Sized {
2929
type Unzipped;
3030
fn unzip_output(output: Output<Self>, builder: &mut Builder) -> Self::Unzipped;
3131

32-
fn make_targets(commands: &mut Commands) -> SmallVec<[Entity; 8]>;
33-
3432
fn distribute_values(request: OperationRequest) -> OperationResult;
3533

3634
type Prepended<T>;
3735
fn prepend<T>(self, value: T) -> Self::Prepended<T>;
3836
}
3937

40-
impl<A: 'static + Send + Sync> Unzippable for (A,) {
41-
type Unzipped = Output<A>;
42-
fn unzip_output(output: Output<Self>, builder: &mut Builder) -> Self::Unzipped {
43-
assert_eq!(output.scope(), builder.scope());
44-
let targets = Self::make_targets(builder.commands);
45-
46-
let result = Output::new(builder.scope, targets[0]);
47-
48-
builder.commands.add(AddOperation::new(
49-
Some(output.scope()),
50-
output.id(),
51-
ForkUnzip::<Self>::new(ForkTargetStorage(targets)),
52-
));
53-
result
54-
}
55-
56-
fn make_targets(commands: &mut Commands) -> SmallVec<[Entity; 8]> {
57-
SmallVec::from_iter([commands.spawn(UnusedTarget).id()])
58-
}
59-
60-
fn distribute_values(
61-
OperationRequest { source, world, roster }: OperationRequest
62-
) -> OperationResult {
63-
let Input { session, data: inputs } = world
64-
.get_entity_mut(source).or_broken()?
65-
.take_input::<Self>()?;
66-
67-
let targets = world.get::<ForkTargetStorage>(source).or_broken()?;
68-
let target = (targets.0)[0];
69-
if let Some(mut t_mut) = world.get_entity_mut(target) {
70-
t_mut.give_input(session, inputs.0, roster)?;
71-
}
72-
Ok(())
73-
}
74-
75-
type Prepended<T> = (T, A);
76-
fn prepend<T>(self, value: T) -> Self::Prepended<T> {
77-
(value, self.0)
78-
}
79-
}
80-
81-
impl<A: 'static + Send + Sync, B: 'static + Send + Sync> Unzippable for (A, B) {
82-
type Unzipped = (Output<A>, Output<B>);
83-
fn unzip_output(output: Output<Self>, builder: &mut Builder) -> Self::Unzipped {
84-
assert_eq!(output.scope(), builder.scope());
85-
let targets = Self::make_targets(builder.commands);
86-
87-
let result = (
88-
Output::new(builder.scope, targets[0]),
89-
Output::new(builder.scope, targets[1]),
90-
);
91-
92-
builder.commands.add(AddOperation::new(
93-
Some(output.scope()),
94-
output.id(),
95-
ForkUnzip::<Self>::new(ForkTargetStorage(targets)),
96-
));
97-
result
98-
}
99-
100-
fn make_targets(commands: &mut Commands) -> SmallVec<[Entity; 8]> {
101-
SmallVec::from_iter([
102-
commands.spawn(UnusedTarget).id(),
103-
commands.spawn(UnusedTarget).id(),
104-
])
105-
}
106-
107-
fn distribute_values(
108-
OperationRequest { source, world, roster }: OperationRequest,
109-
) -> OperationResult {
110-
let Input { session, data: inputs } = world
111-
.get_entity_mut(source).or_broken()?
112-
.take_input::<Self>()?;
113-
114-
let targets = world.get::<ForkTargetStorage>(source).or_broken()?;
115-
let target_0 = *targets.0.get(0).or_broken()?;
116-
let target_1 = *targets.0.get(1).or_broken()?;
117-
118-
if let Some(mut t_mut) = world.get_entity_mut(target_0) {
119-
t_mut.give_input(session, inputs.0, roster)?;
120-
}
121-
122-
if let Some(mut t_mut) = world.get_entity_mut(target_1) {
123-
t_mut.give_input(session, inputs.1, roster)?;
38+
macro_rules! impl_unzippable_for_tuple {
39+
($($T:ident),*) => {
40+
#[allow(non_snake_case)]
41+
impl<$($T: 'static + Send + Sync),*> Unzippable for ($($T,)*)
42+
{
43+
type Unzipped = ($(Output<$T>,)*);
44+
fn unzip_output(output: Output<Self>, builder: &mut Builder) -> Self::Unzipped {
45+
assert_eq!(output.scope(), builder.scope());
46+
let mut targets = SmallVec::new();
47+
let result =
48+
(
49+
$(
50+
{
51+
// Variable is only used to make sure this cycle is repeated once
52+
// for each instance of the $T type, but the type itself is not
53+
// used.
54+
#[allow(unused)]
55+
let $T = std::marker::PhantomData::<$T>;
56+
let target = builder.commands.spawn(UnusedTarget).id();
57+
targets.push(target);
58+
Output::new(builder.scope, target)
59+
},
60+
)*
61+
);
62+
63+
builder.commands.add(AddOperation::new(
64+
Some(output.scope()),
65+
output.id(),
66+
ForkUnzip::<Self>::new(ForkTargetStorage(targets)),
67+
));
68+
result
69+
}
70+
71+
fn distribute_values(
72+
OperationRequest { source, world, roster }: OperationRequest,
73+
) -> OperationResult {
74+
let Input { session, data: inputs } = world
75+
.get_entity_mut(source).or_broken()?
76+
.take_input::<Self>()?;
77+
78+
// Targets is cloned to avoid borrow checker issues when
79+
// doing a mutable borrow of the world later
80+
let targets = world.get::<ForkTargetStorage>(source).or_broken()?.clone();
81+
// The compiler throws a warning when implementing this for
82+
// tuple sizes that wouldn't use the result of the first _idx = _idx + 1
83+
// so we add a leading underscore to suppress the warning
84+
let mut _idx = 0;
85+
let ($($T,)*) = inputs;
86+
$(
87+
let target = *targets.0.get(_idx).or_broken()?;
88+
if let Some(mut t_mut) = world.get_entity_mut(target) {
89+
t_mut.give_input(session, $T, roster)?;
90+
}
91+
_idx = _idx + 1;
92+
)*
93+
Ok(())
94+
}
95+
96+
type Prepended<T> = (T, $($T,)*);
97+
fn prepend<T>(self, value: T) -> Self::Prepended<T> {
98+
let ($($T,)*) = self;
99+
(value, $($T,)*)
100+
}
124101
}
125-
126-
Ok(())
127-
}
128-
129-
type Prepended<T> = (T, A, B);
130-
fn prepend<T>(self, value: T) -> Self::Prepended<T> {
131-
(value, self.0, self.1)
132102
}
133103
}
134104

135-
impl<A, B, C> Unzippable for (A, B, C)
136-
where
137-
A: 'static + Send + Sync,
138-
B: 'static + Send + Sync,
139-
C: 'static + Send + Sync,
140-
{
141-
type Unzipped = (Output<A>, Output<B>, Output<C>);
142-
fn unzip_output(output: Output<Self>, builder: &mut Builder) -> Self::Unzipped {
143-
assert_eq!(output.scope(), builder.scope());
144-
let targets = Self::make_targets(builder.commands);
145-
146-
let result = (
147-
Output::new(builder.scope, targets[0]),
148-
Output::new(builder.scope, targets[1]),
149-
Output::new(builder.scope, targets[2]),
150-
);
151-
152-
builder.commands.add(AddOperation::new(
153-
Some(output.scope()),
154-
output.id(),
155-
ForkUnzip::<Self>::new(ForkTargetStorage(targets)),
156-
));
157-
result
158-
}
159-
160-
fn make_targets(commands: &mut Commands) -> SmallVec<[Entity; 8]> {
161-
SmallVec::from_iter([
162-
commands.spawn(UnusedTarget).id(),
163-
commands.spawn(UnusedTarget).id(),
164-
commands.spawn(UnusedTarget).id(),
165-
])
166-
}
167-
168-
fn distribute_values(
169-
OperationRequest { source, world, roster }: OperationRequest,
170-
) -> OperationResult {
171-
let Input { session, data: inputs } = world
172-
.get_entity_mut(source).or_broken()?
173-
.take_input::<Self>()?;
174-
175-
let targets = world.get::<ForkTargetStorage>(source).or_broken()?;
176-
let target_0 = *targets.0.get(0).or_broken()?;
177-
let target_1 = *targets.0.get(1).or_broken()?;
178-
let target_2 = *targets.0.get(2).or_broken()?;
179-
180-
if let Some(mut t_mut) = world.get_entity_mut(target_0) {
181-
t_mut.give_input(session, inputs.0, roster)?;
182-
}
183-
184-
if let Some(mut t_mut) = world.get_entity_mut(target_1) {
185-
t_mut.give_input(session, inputs.1, roster)?;
186-
}
187-
188-
if let Some(mut t_mut) = world.get_entity_mut(target_2) {
189-
t_mut.give_input(session, inputs.2, roster)?;
190-
}
191-
192-
Ok(())
193-
}
194-
195-
type Prepended<T> = (T, A, B, C);
196-
fn prepend<T>(self, value: T) -> Self::Prepended<T> {
197-
(value, self.0, self.1, self.2)
198-
}
199-
}
105+
// Implements the `Unzippable` trait for all tuples between size 1 and 15
106+
// (inclusive) made of 'static lifetime types that are `Send` and `Sync`
107+
all_tuples!(impl_unzippable_for_tuple, 1, 15, T);
200108

201109
/// A trait for constructs that are able to perform a forking unzip of an
202110
/// unzippable chain. An unzippable chain is one whose response type contains a
@@ -206,37 +114,25 @@ pub trait UnzipBuilder<Z> {
206114
fn unzip_build(self, output: Output<Z>, builder: &mut Builder) -> Self::ReturnType;
207115
}
208116

209-
impl<A, Fa, Ua, B, Fb, Ub> UnzipBuilder<(A, B)> for (Fa, Fb)
210-
where
211-
A: 'static + Send + Sync,
212-
B: 'static + Send + Sync,
213-
Fa: FnOnce(Chain<A>) -> Ua,
214-
Fb: FnOnce(Chain<B>) -> Ub,
215-
{
216-
type ReturnType = (Ua, Ub);
217-
fn unzip_build(self, output: Output<(A, B)>, builder: &mut Builder) -> Self::ReturnType {
218-
let outputs = <(A, B)>::unzip_output(output, builder);
219-
let u_a = (self.0)(outputs.0.chain(builder));
220-
let u_b = (self.1)(outputs.1.chain(builder));
221-
(u_a, u_b)
117+
macro_rules! impl_unzipbuilder_for_tuple {
118+
($(($A:ident, $F:ident, $U:ident)),*) => {
119+
#[allow(non_snake_case)]
120+
impl<$($A: 'static + Send + Sync),*, $($F: FnOnce(Chain<$A>) -> $U),*, $($U),*> UnzipBuilder<($($A,)*)> for ($($F,)*)
121+
{
122+
type ReturnType = ($($U),*);
123+
fn unzip_build(self, output: Output<($($A,)*)>, builder: &mut Builder) -> Self::ReturnType {
124+
let outputs = <($($A),*)>::unzip_output(output, builder);
125+
let ($($A,)*) = outputs;
126+
let ($($F,)*) = self;
127+
(
128+
$(
129+
($F)($A.chain(builder)),
130+
)*
131+
)
132+
}
133+
}
222134
}
223135
}
224136

225-
impl<A, Fa, Ua, B, Fb, Ub, C, Fc, Uc> UnzipBuilder<(A, B, C)> for (Fa, Fb, Fc)
226-
where
227-
A: 'static + Send + Sync,
228-
B: 'static + Send + Sync,
229-
C: 'static + Send + Sync,
230-
Fa: FnOnce(Chain<A>) -> Ua,
231-
Fb: FnOnce(Chain<B>) -> Ub,
232-
Fc: FnOnce(Chain<C>) -> Uc,
233-
{
234-
type ReturnType = (Ua, Ub, Uc);
235-
fn unzip_build(self, output: Output<(A, B, C)>, builder: &mut Builder) -> Self::ReturnType {
236-
let outputs = <(A, B, C)>::unzip_output(output, builder);
237-
let u_a = (self.0)(outputs.0.chain(builder));
238-
let u_b = (self.1)(outputs.1.chain(builder));
239-
let u_c = (self.2)(outputs.2.chain(builder));
240-
(u_a, u_b, u_c)
241-
}
242-
}
137+
// Implements the `UnzipBuilder` trait for all tuples between size 1 and 15
138+
all_tuples!(impl_unzipbuilder_for_tuple, 2, 15, A, F, U);

0 commit comments

Comments
 (0)