bevy_logic/logic/
builder.rs

1use std::marker::PhantomData;
2use bevy::{ ecs::system::EntityCommands, prelude::* };
3use crate::{
4    commands::UpdateOutputWireSet,
5    components::{ GateOutput, InputBundle, LogicGateFans, OutputBundle, Wire, WireBundle },
6    logic::signal::Signal,
7};
8
9/// A builder trait that helps construct logic gate hierarchies and wires.
10pub trait LogicExt {
11    type EntityBuilder<'a> where Self: 'a;
12    type GateBuilder;
13    type WireBuilder;
14    type NoEvalWire;
15
16    /// Spawn a new entity with `bundle` and return a [`GateBuilder`] for further construction.
17    fn spawn_gate(
18        &mut self,
19        bundle: impl Bundle
20    ) -> GateBuilder<'_, Self::GateBuilder, Unknown, Unknown>;
21    fn spawn_input(&mut self) -> Self::EntityBuilder<'_>;
22    fn spawn_output(&mut self) -> Self::EntityBuilder<'_>;
23    fn spawn_wire<I, O>(
24        &mut self,
25        from_gate: &GateData<I, Known>,
26        from_output: usize,
27        to_gate: &GateData<Known, O>,
28        to_input: usize
29    ) -> WireBuilder<'_, Self::WireBuilder>;
30
31    /// Spawn a wire that connects two fans. The output entity **must** have a [`NoEvalOutput`] component
32    /// and not require evaluation or ordering in the [`LogicGraph`] resource.
33    fn spawn_no_eval_wire(&mut self, from_output: Entity, to_input: Entity) -> Entity;
34}
35
36impl LogicExt for World {
37    type EntityBuilder<'a> = EntityWorldMut<'a>;
38    type GateBuilder = Self;
39    type WireBuilder = Self;
40    type NoEvalWire = Entity;
41
42    fn spawn_gate(&mut self, bundle: impl Bundle) -> GateBuilder<'_, Self::GateBuilder> {
43        let entity = self.spawn(bundle).id();
44        GateBuilder {
45            cmd: self,
46            data: GateData {
47                entity,
48                fans: LogicGateFans::default(),
49                _state: PhantomData,
50            },
51        }
52    }
53
54    fn spawn_input(&mut self) -> Self::EntityBuilder<'_> {
55        self.spawn(InputBundle::default())
56    }
57
58    fn spawn_output(&mut self) -> Self::EntityBuilder<'_> {
59        self.spawn(OutputBundle::default())
60    }
61
62    /// Create a wire `from_gate` at `from_output` to `to_gate` at `to_input`,
63    /// then update the gate output's `wires` set with the new wire entity.
64    ///
65    /// # Panics
66    ///
67    /// Panics if the input/output index is out of bounds, or if the input/output entity at `index` is `None`.
68    fn spawn_wire<I, O>(
69        &mut self,
70        from_gate: &GateData<I, Known>,
71        from_output: usize,
72        to_gate: &GateData<Known, O>,
73        to_input: usize
74    ) -> WireBuilder<'_, Self::WireBuilder> {
75        let from = from_gate.output(from_output);
76        let to = to_gate.input(to_input);
77        let entity = self.spawn((Signal::Undefined, Wire::new(from, to))).id();
78
79        self.get_mut::<GateOutput>(from)
80            .expect("from_gate entity does not have GateOutput component")
81            .wires.insert(entity);
82
83        WireBuilder {
84            cmd: self,
85            data: WireData {
86                entity,
87                from,
88                to,
89                from_gate: from_gate.id(),
90                to_gate: to_gate.id(),
91            },
92        }
93    }
94
95    fn spawn_no_eval_wire(&mut self, from_output: Entity, to_input: Entity) -> Entity {
96        let wire_entity = self
97            .spawn(WireBundle {
98                wire: Wire {
99                    from: from_output,
100                    to: to_input,
101                },
102                signal: Signal::default(),
103            })
104            .id();
105
106        self.get_mut::<GateOutput>(from_output)
107            .expect("from_output entity does not have GateOutput component")
108            .wires.insert(wire_entity);
109
110        wire_entity
111    }
112}
113
114impl<'w, 's> LogicExt for Commands<'w, 's> {
115    type EntityBuilder<'a> = EntityCommands<'a> where Self: 'a;
116    type GateBuilder = Self;
117    type WireBuilder = Self;
118    type NoEvalWire = Entity;
119
120    fn spawn_gate(&mut self, bundle: impl Bundle) -> GateBuilder<'_, Self::GateBuilder> {
121        let entity = self.spawn(bundle).id();
122        GateBuilder {
123            cmd: self,
124            data: GateData {
125                entity,
126                fans: LogicGateFans::default(),
127                _state: PhantomData,
128            },
129        }
130    }
131
132    fn spawn_input(&mut self) -> Self::EntityBuilder<'_> {
133        self.spawn(InputBundle::default())
134    }
135
136    fn spawn_output(&mut self) -> Self::EntityBuilder<'_> {
137        self.spawn(OutputBundle::default())
138    }
139
140    /// Create a wire `from_gate` at `from_output` to `to_gate` at `to_input`,
141    /// then update the gate output's `wires` set with the new wire entity.
142    ///
143    /// # Panics
144    ///
145    /// Panics if the input/output index is out of bounds, or if the input/output entity at `index` is `None`.
146    fn spawn_wire<I, O>(
147        &mut self,
148        from_gate: &GateData<I, Known>,
149        from_output: usize,
150        to_gate: &GateData<Known, O>,
151        to_input: usize
152    ) -> WireBuilder<'_, Self::WireBuilder> {
153        let from = from_gate.output(from_output);
154        let to = to_gate.input(to_input);
155        let entity = self.spawn((Signal::Undefined, Wire::new(from, to))).id();
156
157        self.add(UpdateOutputWireSet::Add { output_entity: from, wire_entity: entity });
158
159        WireBuilder {
160            cmd: self,
161            data: WireData {
162                entity,
163                from,
164                to,
165                from_gate: from_gate.id(),
166                to_gate: to_gate.id(),
167            },
168        }
169    }
170
171    fn spawn_no_eval_wire(&mut self, from_output: Entity, to_input: Entity) -> Entity {
172        let wire_entity = self
173            .spawn(WireBundle {
174                wire: Wire {
175                    from: from_output,
176                    to: to_input,
177                },
178                signal: Signal::default(),
179            })
180            .id();
181
182        self.add(UpdateOutputWireSet::Add {
183            output_entity: from_output,
184            wire_entity,
185        });
186
187        wire_entity
188    }
189}
190
191#[derive(Debug, Clone, Copy)]
192pub struct Unknown;
193
194#[derive(Debug, Clone, Copy)]
195pub struct Known;
196
197#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct GateData<I = Unknown, O = Unknown> {
199    entity: Entity,
200    fans: LogicGateFans,
201    _state: PhantomData<(I, O)>,
202}
203
204impl<I, O> GateData<I, O> {
205    pub fn id(&self) -> Entity {
206        self.entity
207    }
208
209    pub fn inputs(&self) -> &[Option<Entity>] {
210        &self.fans.inputs
211    }
212
213    pub fn outputs(&self) -> &[Option<Entity>] {
214        &self.fans.outputs
215    }
216}
217
218impl<O> GateData<Known, O> {
219    /// Get the input entity at `index`.
220    ///
221    /// Returns `None` if the index is out of bounds or if the input entity is `None`.
222    pub fn get_input(&self, index: usize) -> Option<Entity> {
223        self.fans.inputs.get(index).copied().flatten()
224    }
225
226    /// # Panics
227    ///
228    /// Panics if the input index is out of bounds, or if the input entity at `index` is `None`.
229    pub fn input(&self, index: usize) -> Entity {
230        self.fans.inputs[index].expect("input entity is None")
231    }
232}
233
234impl<I> GateData<I, Known> {
235    /// Get the output entity at `index`.
236    ///
237    /// Returns `None` if the index is out of bounds or if the output entity is `None`.
238    pub fn get_output(&self, index: usize) -> Option<Entity> {
239        self.fans.outputs.get(index).copied().flatten()
240    }
241
242    /// # Panics
243    ///
244    /// Panics if the output index is out of bounds, or if the input entity at `index` is `None`.
245    pub fn output(&self, index: usize) -> Entity {
246        self.fans.outputs[index].expect("input entity is None")
247    }
248}
249
250pub struct GateBuilder<'a, T, I = Unknown, O = Unknown> {
251    cmd: &'a mut T,
252    data: GateData<I, O>,
253}
254
255/// A trait that provides mutable access to an [`EntityWorldMut`] and its child index in the range `0..count`.
256pub trait GateFanWorldMut {
257    fn modify_fan(&mut self, cmd: &mut EntityWorldMut, index: usize);
258}
259impl<T> GateFanWorldMut for T where T: FnMut(&mut EntityWorldMut, usize) {
260    fn modify_fan(&mut self, cmd: &mut EntityWorldMut, index: usize) {
261        self(cmd, index);
262    }
263}
264
265pub trait GateFanEntityMut {
266    fn modify_fan(&mut self, cmd: &mut EntityCommands, index: usize);
267}
268
269impl<T> GateFanEntityMut for T where T: FnMut(&mut EntityCommands, usize) {
270    fn modify_fan(&mut self, cmd: &mut EntityCommands, index: usize) {
271        self(cmd, index);
272    }
273}
274
275impl<'a, I, O> GateBuilder<'a, World, I, O> {
276    pub fn world(&mut self) -> &mut World {
277        self.cmd
278    }
279
280    pub fn entity_commands(&mut self) -> EntityWorldMut<'_> {
281        self.cmd.entity_mut(self.data.entity)
282    }
283
284    pub fn insert_bundle(mut self, bundle: impl Bundle) -> Self {
285        self.entity_commands().insert(bundle);
286        self
287    }
288
289    pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
290        self.entity_commands().insert(bundle);
291        self
292    }
293}
294
295impl<'a, O> GateBuilder<'a, World, Unknown, O> {
296    pub fn with_inputs(self, count: usize) -> GateBuilder<'a, World, Known, O> {
297        let mut inputs = Vec::with_capacity(count);
298        self.cmd.entity_mut(self.data.entity).with_children(|gate| {
299            for _ in 0..count {
300                inputs.push(Some(gate.spawn(InputBundle::default()).id()));
301            }
302        });
303
304        GateBuilder {
305            cmd: self.cmd,
306            data: GateData {
307                entity: self.data.entity,
308                fans: LogicGateFans {
309                    inputs,
310                    outputs: self.data.fans.outputs,
311                },
312                _state: PhantomData,
313            },
314        }
315    }
316
317    /// Build `count` input entities and use `builder` on each entity. Provides
318    /// access to the input [`EntityWorldMut`] and its index in the range `0..count`.
319    pub fn build_inputs(
320        self,
321        count: usize,
322        mut builder: impl GateFanWorldMut
323    ) -> GateBuilder<'a, World, Known, O> {
324        let mut inputs = Vec::with_capacity(count);
325
326        self.cmd.entity_mut(self.data.entity).with_children(|gate| {
327            for i in 0..count {
328                let mut cmd = gate.spawn(InputBundle::default());
329                let input_entity = cmd.id();
330                inputs.push(Some(input_entity));
331                builder.modify_fan(&mut cmd, i);
332            }
333        });
334
335        GateBuilder {
336            cmd: self.cmd,
337            data: GateData {
338                entity: self.data.entity,
339                fans: LogicGateFans {
340                    inputs,
341                    outputs: self.data.fans.outputs,
342                },
343                _state: PhantomData,
344            },
345        }
346    }
347}
348
349impl<'a, I> GateBuilder<'a, World, I, Unknown> {
350    pub fn with_outputs(self, count: usize) -> GateBuilder<'a, World, I, Known> {
351        let mut outputs = Vec::with_capacity(count);
352        self.cmd.entity_mut(self.data.entity).with_children(|gate| {
353            for _ in 0..count {
354                outputs.push(Some(gate.spawn(OutputBundle::default()).id()));
355            }
356        });
357
358        GateBuilder {
359            cmd: self.cmd,
360            data: GateData {
361                entity: self.data.entity,
362                fans: LogicGateFans {
363                    inputs: self.data.fans.inputs,
364                    outputs,
365                },
366                _state: PhantomData,
367            },
368        }
369    }
370
371    /// Build `count` output entities and call `builder` on each entity. Provides
372    /// access to the output [`EntityWorldMut`] and its index in the range `0..count`.
373    pub fn build_outputs(
374        self,
375        count: usize,
376        mut builder: impl GateFanWorldMut
377    ) -> GateBuilder<'a, World, I, Known> {
378        let mut outputs = Vec::with_capacity(count);
379
380        self.cmd.entity_mut(self.data.entity).with_children(|gate| {
381            for i in 0..count {
382                let mut cmd = gate.spawn(OutputBundle::default());
383                let output_entity = cmd.id();
384                outputs.push(Some(output_entity));
385                builder.modify_fan(&mut cmd, i);
386            }
387        });
388
389        GateBuilder {
390            cmd: self.cmd,
391            data: GateData {
392                entity: self.data.entity,
393                fans: LogicGateFans {
394                    inputs: self.data.fans.inputs,
395                    outputs,
396                },
397                _state: PhantomData,
398            },
399        }
400    }
401}
402
403impl<'a, I, O> GateBuilder<'a, World, I, O> {
404    /// Finalize construction of the gate hierarchy, link children, and insert a [`LogicEntity`]
405    /// component into the root entity from [`Self::data`].
406    ///
407    /// Returns [`Self::data`], which can be used to wire inputs/outputs together
408    /// by their [`Entity`] IDs and link gates in a logic graph.
409    pub fn build(self) -> GateData<I, O> {
410        self.cmd
411            .entity_mut(self.data.entity)
412            .push_children(
413                &self.data.fans
414                    .some_inputs()
415                    .into_iter()
416                    .chain(self.data.fans.some_outputs())
417                    .collect::<Vec<_>>()
418            )
419            .insert(self.data.fans.clone());
420
421        self.data
422    }
423}
424
425//* Gate builder for `Commands` */
426
427impl<'w, 's, 'a, I, O> GateBuilder<'a, Commands<'w, 's>, I, O> {
428    pub fn entity_commands(&mut self) -> EntityCommands<'_> {
429        self.cmd.entity(self.data.entity)
430    }
431
432    pub fn insert_bundle(mut self, bundle: impl Bundle) -> Self {
433        self.entity_commands().insert(bundle);
434        self
435    }
436
437    pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
438        self.entity_commands().insert(bundle);
439        self
440    }
441}
442
443impl<'w, 's, 'a, O> GateBuilder<'a, Commands<'w, 's>, Unknown, O> {
444    pub fn with_inputs(self, count: usize) -> GateBuilder<'a, Commands<'w, 's>, Known, O> {
445        let mut inputs = Vec::with_capacity(count);
446        self.cmd.entity(self.data.entity).with_children(|gate| {
447            for _ in 0..count {
448                inputs.push(Some(gate.spawn(InputBundle::default()).id()));
449            }
450        });
451
452        GateBuilder {
453            cmd: self.cmd,
454            data: GateData {
455                entity: self.data.entity,
456                fans: LogicGateFans {
457                    inputs,
458                    outputs: self.data.fans.outputs,
459                },
460                _state: PhantomData,
461            },
462        }
463    }
464
465    /// Build `count` input entities and use `builder` on each entity. Provides
466    /// access to the input [`EntityWorldMut`] and its index in the range `0..count`.
467    pub fn build_inputs(
468        self,
469        count: usize,
470        mut builder: impl GateFanEntityMut
471    ) -> GateBuilder<'a, Commands<'w, 's>, Known, O> {
472        let mut inputs = Vec::with_capacity(count);
473
474        self.cmd.entity(self.data.entity).with_children(|gate| {
475            for i in 0..count {
476                let mut cmd = gate.spawn(InputBundle::default());
477                let input_entity = cmd.id();
478                inputs.push(Some(input_entity));
479                builder.modify_fan(&mut cmd, i);
480            }
481        });
482
483        GateBuilder {
484            cmd: self.cmd,
485            data: GateData {
486                entity: self.data.entity,
487                fans: LogicGateFans {
488                    inputs,
489                    outputs: self.data.fans.outputs,
490                },
491                _state: PhantomData,
492            },
493        }
494    }
495}
496
497impl<'w, 's, 'a, I> GateBuilder<'a, Commands<'w, 's>, I, Unknown> {
498    pub fn with_outputs(self, count: usize) -> GateBuilder<'a, Commands<'w, 's>, I, Known> {
499        let mut outputs = Vec::with_capacity(count);
500        self.cmd.entity(self.data.entity).with_children(|gate| {
501            for _ in 0..count {
502                outputs.push(Some(gate.spawn(OutputBundle::default()).id()));
503            }
504        });
505
506        GateBuilder {
507            cmd: self.cmd,
508            data: GateData {
509                entity: self.data.entity,
510                fans: LogicGateFans {
511                    inputs: self.data.fans.inputs,
512                    outputs,
513                },
514                _state: PhantomData,
515            },
516        }
517    }
518
519    /// Build `count` output entities and call `builder` on each entity. Provides
520    /// access to the output [`EntityWorldMut`] and its index in the range `0..count`.
521    pub fn build_outputs(
522        self,
523        count: usize,
524        mut builder: impl GateFanEntityMut
525    ) -> GateBuilder<'a, Commands<'w, 's>, I, Known> {
526        let mut outputs = Vec::with_capacity(count);
527
528        self.cmd.entity(self.data.entity).with_children(|gate| {
529            for i in 0..count {
530                let mut cmd = gate.spawn(OutputBundle::default());
531                let output_entity = cmd.id();
532                outputs.push(Some(output_entity));
533                builder.modify_fan(&mut cmd, i);
534            }
535        });
536
537        GateBuilder {
538            cmd: self.cmd,
539            data: GateData {
540                entity: self.data.entity,
541                fans: LogicGateFans {
542                    inputs: self.data.fans.inputs,
543                    outputs,
544                },
545                _state: PhantomData,
546            },
547        }
548    }
549}
550
551impl<'w, 's, 'a, I, O> GateBuilder<'a, Commands<'w, 's>, I, O> {
552    /// Finalize construction of the gate hierarchy, link children, and insert a [`LogicEntity`]
553    /// component into the root entity from [`Self::data`].
554    ///
555    /// Returns [`Self::data`], which can be used to wire inputs/outputs together
556    /// by their [`Entity`] IDs and link gates in a logic graph.
557    pub fn build(self) -> GateData<I, O> {
558        self.cmd
559            .entity(self.data.entity)
560            .push_children(
561                &self.data.fans
562                    .some_inputs()
563                    .into_iter()
564                    .chain(self.data.fans.some_outputs())
565                    .collect::<Vec<_>>()
566            )
567            .insert(self.data.fans.clone());
568
569        self.data
570    }
571}
572
573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub struct WireData {
575    pub entity: Entity,
576    pub from: Entity,
577    pub from_gate: Entity,
578    pub to: Entity,
579    pub to_gate: Entity,
580}
581
582impl WireData {
583    #[inline]
584    pub const fn id(&self) -> Entity {
585        self.entity
586    }
587
588    #[doc(alias = "from")]
589    #[doc(alias = "sink")]
590    #[inline]
591    pub const fn input(&self) -> Entity {
592        self.from
593    }
594
595    #[doc(alias = "to")]
596    #[doc(alias = "source")]
597    #[inline]
598    pub const fn output(&self) -> Entity {
599        self.to
600    }
601}
602
603pub struct WireBuilder<'a, T> {
604    cmd: &'a mut T,
605    data: WireData,
606}
607
608impl<T> WireBuilder<'_, T> {
609    pub fn world(&mut self) -> &mut T {
610        self.cmd
611    }
612
613    /// Returns the [`Entity`] ID of the wire.
614    pub const fn id(&self) -> Entity {
615        self.data.entity
616    }
617
618    /// Returns the [`Entity`] associated with the `from` node of the wire.
619    #[doc(alias = "from")]
620    #[doc(alias = "sink")]
621    #[inline]
622    pub const fn input(&self) -> Entity {
623        self.data.from
624    }
625
626    /// Returns the [`Entity`] associated with the `to` node of the wire.
627    #[doc(alias = "to")]
628    #[doc(alias = "source")]
629    #[inline]
630    pub const fn output(&self) -> Entity {
631        self.data.to
632    }
633}
634
635impl WireBuilder<'_, World> {
636    /// Downgrade the builder into a [`WireData`] instance,
637    /// dropping the mutable reference to the world.
638    pub fn downgrade(self) -> WireData {
639        self.data
640    }
641
642    pub fn entity_commands(&mut self) -> EntityWorldMut<'_> {
643        self.cmd.entity_mut(self.data.entity)
644    }
645
646    pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
647        self.entity_commands().insert(bundle);
648        self
649    }
650}
651
652impl<'w, 's> WireBuilder<'_, Commands<'w, 's>> {
653    /// Downgrade the builder into a [`WireData`] instance,
654    /// dropping the mutable reference to the world.
655    pub fn downgrade(self) -> WireData {
656        self.data
657    }
658
659    pub fn entity_commands(&mut self) -> EntityCommands<'_> {
660        self.cmd.entity(self.data.entity)
661    }
662
663    pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
664        self.entity_commands().insert(bundle);
665        self
666    }
667}