Skip to main content

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}