pumpkin_core/propagation/event_registration.rs
1use enumset::EnumSet;
2
3use crate::propagation::DomainEvent;
4use crate::propagation::DomainEvents;
5use crate::propagation::LocalId;
6use crate::variables::DomainId;
7
8/// Anything that can subscribe to domain events.
9///
10/// Typically these are variables.
11pub trait EventTarget {
12 /// Indicate that `self` should be registered for the given domain events as the given local ID.
13 fn register(
14 &self,
15 registration: &mut impl EventDispatcher,
16 events: EnumSet<DomainEvent>,
17 local_id: LocalId,
18 );
19}
20
21/// The interface to a component that needs to know which variables care about which events.
22pub trait EventDispatcher {
23 // This is a separate trait to isolate the event registration from the rest of the solver.
24 // That isolation is beneficial when writing tests, as individual components can easily be
25 // mocked without needing to set up an entire state/solver.
26
27 /// Register the [`DomainId`] with the given [`LocalId`] on the given [`DomainEvents`].
28 ///
29 /// It is possible to register the same [`DomainId`] with different [`LocalId`]s. This may
30 /// happen when the propagator uses multiple local IDs for different events for the same
31 /// variable. Or when different views over the same domain are used in a propagator.
32 fn register(&mut self, domain_id: DomainId, events: EnumSet<DomainEvent>, local_id: LocalId);
33}
34
35/// Contains all the events and domains that a propagator needs to be enqueued for.
36#[derive(Clone, Debug)]
37pub struct EventsToRegister(Vec<(DomainId, EnumSet<DomainEvent>, LocalId)>);
38
39impl EventsToRegister {
40 /// Create an [`EventsToRegister`] without any variables.
41 ///
42 /// This is the uncommon case. Without registering for variable events, a propagator will never
43 /// be enqueued automatically. However, certain propagators like, e.g., compound propagators,
44 /// may not be able to register during construction in which case they will be enqueued
45 /// explicitly when a constraint is added to them. The nogood propagator is an example of such a
46 /// propagator.
47 pub fn empty() -> EventsToRegister {
48 EventsToRegister(vec![])
49 }
50
51 /// Create a new [`EventsToRegisterBuilder`].
52 ///
53 /// If no event registrations will be made, use [`EventsToRegister::empty`] instead.
54 /// Calling [`EventsToRegisterBuilder::build`] without any registrations will cause a panic.
55 pub fn builder() -> EventsToRegisterBuilder {
56 EventsToRegisterBuilder {
57 registrations: EventsToRegister(vec![]),
58 }
59 }
60
61 /// Add a new event registration to an existing instance of self.
62 ///
63 /// # Example
64 ///
65 /// ```
66 /// use pumpkin_core::propagation::DomainEvents;
67 /// use pumpkin_core::propagation::EventsToRegister;
68 /// use pumpkin_core::propagation::LocalId;
69 /// use pumpkin_core::variables::DomainId;
70 ///
71 /// let v1 = DomainId::new(0);
72 /// let v2 = DomainId::new(0);
73 /// let mut registration = EventsToRegister::builder()
74 /// .add(&v1, DomainEvents::ANY_INT, LocalId::from(0))
75 /// .build();
76 ///
77 /// // Extend the events to register with another variable.
78 /// registration.add(&v2, DomainEvents::ANY_INT, LocalId::from(1));
79 /// ```
80 pub fn add(
81 &mut self,
82 target: &impl EventTarget,
83 domain_events: DomainEvents,
84 local_id: LocalId,
85 ) {
86 target.register(self, domain_events.events(), local_id);
87 }
88
89 /// Iterate the registrations already made.
90 pub fn iter(&self) -> impl ExactSizeIterator<Item = (DomainId, EnumSet<DomainEvent>, LocalId)> {
91 self.0.iter().copied()
92 }
93}
94
95impl EventDispatcher for EventsToRegister {
96 fn register(&mut self, domain_id: DomainId, events: EnumSet<DomainEvent>, local_id: LocalId) {
97 self.0.push((domain_id, events, local_id));
98 }
99}
100
101/// Used to construct an [`EventsToRegister`] for heterogeneous [`EventTarget`] implementations.
102///
103/// See [`EventsToRegister::builder`] for a usage example.
104#[derive(Clone, Debug)]
105pub struct EventsToRegisterBuilder {
106 registrations: EventsToRegister,
107}
108
109impl EventsToRegisterBuilder {
110 /// Add a new event registration.
111 ///
112 /// # Example
113 ///
114 /// ```
115 /// use pumpkin_core::propagation::DomainEvents;
116 /// use pumpkin_core::propagation::EventsToRegister;
117 /// use pumpkin_core::propagation::LocalId;
118 /// use pumpkin_core::variables::DomainId;
119 ///
120 /// let v1 = DomainId::new(0);
121 /// let v2 = DomainId::new(0);
122 /// let registration = EventsToRegister::builder()
123 /// .add(&v1, DomainEvents::ANY_INT, LocalId::from(0))
124 /// .add(&v2, DomainEvents::ANY_INT, LocalId::from(1))
125 /// .build();
126 /// ```
127 pub fn add(
128 mut self,
129 target: &impl EventTarget,
130 domain_events: DomainEvents,
131 local_id: LocalId,
132 ) -> Self {
133 self.registrations.add(target, domain_events, local_id);
134 self
135 }
136
137 /// Finish constructing the [`EventsToRegister`].
138 ///
139 /// If no variables are registered, then this panics. If no variables can be registered during
140 /// construction, use [`EventsToRegister::empty`].
141 pub fn build(self) -> EventsToRegister {
142 assert!(
143 !self.registrations.0.is_empty(),
144 "did not register for any events"
145 );
146
147 self.registrations
148 }
149}