1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
13pub enum Boundary {
14 #[default]
18 Error,
19 Clamp,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
48pub struct BoundaryPolicy {
49 x_below: Boundary,
50 x_above: Boundary,
51 y_below: Boundary,
52 y_above: Boundary,
53}
54
55impl BoundaryPolicy {
56 #[must_use]
61 pub const fn new() -> Self {
62 Self {
63 x_below: Boundary::Error,
64 x_above: Boundary::Error,
65 y_below: Boundary::Error,
66 y_above: Boundary::Error,
67 }
68 }
69
70 #[must_use]
72 pub const fn with_x_below(self, boundary: Boundary) -> Self {
73 Self {
74 x_below: boundary,
75 ..self
76 }
77 }
78
79 #[must_use]
81 pub const fn with_x_above(self, boundary: Boundary) -> Self {
82 Self {
83 x_above: boundary,
84 ..self
85 }
86 }
87
88 #[must_use]
90 pub const fn with_y_below(self, boundary: Boundary) -> Self {
91 Self {
92 y_below: boundary,
93 ..self
94 }
95 }
96
97 #[must_use]
99 pub const fn with_y_above(self, boundary: Boundary) -> Self {
100 Self {
101 y_above: boundary,
102 ..self
103 }
104 }
105
106 #[must_use]
108 pub const fn x_below(&self) -> Boundary {
109 self.x_below
110 }
111
112 #[must_use]
114 pub const fn x_above(&self) -> Boundary {
115 self.x_above
116 }
117
118 #[must_use]
120 pub const fn y_below(&self) -> Boundary {
121 self.y_below
122 }
123
124 #[must_use]
126 pub const fn y_above(&self) -> Boundary {
127 self.y_above
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::{Boundary, BoundaryPolicy};
134 use core::mem::size_of;
135
136 #[test]
137 fn default_policy_rejects_every_side() {
138 let policy = BoundaryPolicy::default();
139
140 assert_eq!(policy.x_below(), Boundary::Error);
141 assert_eq!(policy.x_above(), Boundary::Error);
142 assert_eq!(policy.y_below(), Boundary::Error);
143 assert_eq!(policy.y_above(), Boundary::Error);
144 }
145
146 #[test]
147 fn default_trait_matches_the_const_constructor() {
148 assert_eq!(BoundaryPolicy::default(), BoundaryPolicy::new());
149 assert_eq!(Boundary::default(), Boundary::Error);
150 }
151
152 #[test]
153 fn each_builder_method_changes_only_its_own_side() {
154 let base = BoundaryPolicy::new();
155
156 let x_below = base.with_x_below(Boundary::Clamp);
157 assert_eq!(x_below.x_below(), Boundary::Clamp);
158 assert_eq!(x_below.x_above(), Boundary::Error);
159 assert_eq!(x_below.y_below(), Boundary::Error);
160 assert_eq!(x_below.y_above(), Boundary::Error);
161
162 let x_above = base.with_x_above(Boundary::Clamp);
163 assert_eq!(x_above.x_below(), Boundary::Error);
164 assert_eq!(x_above.x_above(), Boundary::Clamp);
165 assert_eq!(x_above.y_below(), Boundary::Error);
166 assert_eq!(x_above.y_above(), Boundary::Error);
167
168 let y_below = base.with_y_below(Boundary::Clamp);
169 assert_eq!(y_below.x_below(), Boundary::Error);
170 assert_eq!(y_below.x_above(), Boundary::Error);
171 assert_eq!(y_below.y_below(), Boundary::Clamp);
172 assert_eq!(y_below.y_above(), Boundary::Error);
173
174 let y_above = base.with_y_above(Boundary::Clamp);
175 assert_eq!(y_above.x_below(), Boundary::Error);
176 assert_eq!(y_above.x_above(), Boundary::Error);
177 assert_eq!(y_above.y_below(), Boundary::Error);
178 assert_eq!(y_above.y_above(), Boundary::Clamp);
179 }
180
181 #[test]
182 fn builders_are_usable_in_const_context() {
183 const POLICY: BoundaryPolicy = BoundaryPolicy::new()
184 .with_x_below(Boundary::Clamp)
185 .with_x_above(Boundary::Clamp)
186 .with_y_below(Boundary::Clamp)
187 .with_y_above(Boundary::Clamp);
188
189 assert_eq!(POLICY.x_below(), Boundary::Clamp);
190 assert_eq!(POLICY.x_above(), Boundary::Clamp);
191 assert_eq!(POLICY.y_below(), Boundary::Clamp);
192 assert_eq!(POLICY.y_above(), Boundary::Clamp);
193 }
194
195 #[test]
196 fn the_policy_is_four_single_byte_selections() {
197 assert_eq!(size_of::<Boundary>(), 1);
198 assert_eq!(size_of::<BoundaryPolicy>(), 4);
199 }
200}