Skip to main content

goud_engine/ecs/schedule/
system_ordering.rs

1//! System ordering constraint types.
2
3use std::fmt;
4
5use crate::ecs::system::SystemId;
6
7/// Specifies an ordering constraint between two systems.
8///
9/// Ordering constraints are used to ensure systems run in a specific order,
10/// regardless of the order they were added to the stage.
11///
12/// # Example
13///
14/// ```
15/// use goud_engine::ecs::schedule::SystemOrdering;
16/// use goud_engine::ecs::system::SystemId;
17///
18/// let ordering = SystemOrdering::Before {
19///     system: SystemId::from_raw(1),
20///     before: SystemId::from_raw(2),
21/// };
22/// ```
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum SystemOrdering {
25    /// The `system` must run before `before`.
26    Before {
27        /// The system that must run first.
28        system: SystemId,
29        /// The system that must run after.
30        before: SystemId,
31    },
32    /// The `system` must run after `after`.
33    After {
34        /// The system that must run second.
35        system: SystemId,
36        /// The system that must run first.
37        after: SystemId,
38    },
39}
40
41impl SystemOrdering {
42    /// Creates a constraint where `system` runs before `other`.
43    #[inline]
44    pub fn before(system: SystemId, other: SystemId) -> Self {
45        SystemOrdering::Before {
46            system,
47            before: other,
48        }
49    }
50
51    /// Creates a constraint where `system` runs after `other`.
52    #[inline]
53    pub fn after(system: SystemId, other: SystemId) -> Self {
54        SystemOrdering::After {
55            system,
56            after: other,
57        }
58    }
59
60    /// Returns the system that must run first according to this constraint.
61    #[inline]
62    pub fn first(&self) -> SystemId {
63        match self {
64            SystemOrdering::Before { system, .. } => *system,
65            SystemOrdering::After { after, .. } => *after,
66        }
67    }
68
69    /// Returns the system that must run second according to this constraint.
70    #[inline]
71    pub fn second(&self) -> SystemId {
72        match self {
73            SystemOrdering::Before { before, .. } => *before,
74            SystemOrdering::After { system, .. } => *system,
75        }
76    }
77
78    /// Returns the edge (from, to) for the dependency graph.
79    ///
80    /// The edge represents "from must run before to".
81    #[inline]
82    pub fn as_edge(&self) -> (SystemId, SystemId) {
83        (self.first(), self.second())
84    }
85
86    /// Returns true if this ordering involves the given system.
87    #[inline]
88    pub fn involves(&self, id: SystemId) -> bool {
89        self.first() == id || self.second() == id
90    }
91}
92
93impl fmt::Display for SystemOrdering {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match self {
96            SystemOrdering::Before { system, before } => {
97                write!(f, "System {} before {}", system.raw(), before.raw())
98            }
99            SystemOrdering::After { system, after } => {
100                write!(f, "System {} after {}", system.raw(), after.raw())
101            }
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use std::collections::HashSet;
110
111    #[test]
112    fn test_ordering_before() {
113        let a = SystemId::from_raw(1);
114        let b = SystemId::from_raw(2);
115        let ordering = SystemOrdering::before(a, b);
116        assert_eq!(ordering.first(), a);
117        assert_eq!(ordering.second(), b);
118        assert_eq!(ordering.as_edge(), (a, b));
119    }
120
121    #[test]
122    fn test_ordering_after() {
123        let a = SystemId::from_raw(1);
124        let b = SystemId::from_raw(2);
125        let ordering = SystemOrdering::after(a, b);
126        assert_eq!(ordering.first(), b);
127        assert_eq!(ordering.second(), a);
128        assert_eq!(ordering.as_edge(), (b, a));
129    }
130
131    #[test]
132    fn test_ordering_involves() {
133        let a = SystemId::from_raw(1);
134        let b = SystemId::from_raw(2);
135        let c = SystemId::from_raw(3);
136        let ordering = SystemOrdering::before(a, b);
137        assert!(ordering.involves(a));
138        assert!(ordering.involves(b));
139        assert!(!ordering.involves(c));
140    }
141
142    #[test]
143    fn test_ordering_display() {
144        let a = SystemId::from_raw(1);
145        let b = SystemId::from_raw(2);
146        let before = SystemOrdering::before(a, b);
147        let after = SystemOrdering::after(a, b);
148        let before_str = format!("{}", before);
149        let after_str = format!("{}", after);
150        assert!(before_str.contains("before"));
151        assert!(after_str.contains("after"));
152    }
153
154    #[test]
155    fn test_ordering_equality() {
156        let a = SystemId::from_raw(1);
157        let b = SystemId::from_raw(2);
158        let o1 = SystemOrdering::before(a, b);
159        let o2 = SystemOrdering::before(a, b);
160        let o3 = SystemOrdering::before(b, a);
161        assert_eq!(o1, o2);
162        assert_ne!(o1, o3);
163    }
164
165    #[test]
166    fn test_ordering_hash() {
167        let a = SystemId::from_raw(1);
168        let b = SystemId::from_raw(2);
169        let mut set = HashSet::new();
170        set.insert(SystemOrdering::before(a, b));
171        set.insert(SystemOrdering::before(a, b));
172        set.insert(SystemOrdering::before(b, a));
173        assert_eq!(set.len(), 2);
174    }
175
176    #[test]
177    fn test_ordering_clone() {
178        let a = SystemId::from_raw(1);
179        let b = SystemId::from_raw(2);
180        let o1 = SystemOrdering::before(a, b);
181        let o2 = o1;
182        assert_eq!(o1, o2);
183    }
184}