1use crate::geometry::{Rect, Size};
4use crate::style::{Dimension, LengthPercentage, LengthPercentageAuto};
5use crate::style_helpers::TaffyZero;
6
7pub(crate) trait MaybeResolve<In, Out> {
13 fn maybe_resolve(self, context: In) -> Out;
15}
16
17pub(crate) trait ResolveOrZero<TContext, TOutput: TaffyZero> {
23 fn resolve_or_zero(self, context: TContext) -> TOutput;
25}
26
27impl MaybeResolve<Option<f32>, Option<f32>> for LengthPercentage {
28 fn maybe_resolve(self, context: Option<f32>) -> Option<f32> {
31 match self {
32 LengthPercentage::Length(length) => Some(length),
33 LengthPercentage::Percent(percent) => context.map(|dim| dim * percent),
34 }
35 }
36}
37
38impl MaybeResolve<Option<f32>, Option<f32>> for LengthPercentageAuto {
39 fn maybe_resolve(self, context: Option<f32>) -> Option<f32> {
42 match self {
43 LengthPercentageAuto::Length(length) => Some(length),
44 LengthPercentageAuto::Percent(percent) => context.map(|dim| dim * percent),
45 LengthPercentageAuto::Auto => None,
46 }
47 }
48}
49
50impl MaybeResolve<Option<f32>, Option<f32>> for Dimension {
51 fn maybe_resolve(self, context: Option<f32>) -> Option<f32> {
55 match self {
56 Dimension::Length(length) => Some(length),
57 Dimension::Percent(percent) => context.map(|dim| dim * percent),
58 Dimension::Auto => None,
59 }
60 }
61}
62
63impl<T: MaybeResolve<Option<f32>, Option<f32>>> MaybeResolve<f32, Option<f32>> for T {
66 fn maybe_resolve(self, context: f32) -> Option<f32> {
69 self.maybe_resolve(Some(context))
70 }
71}
72
73impl<In, Out, T: MaybeResolve<In, Out>> MaybeResolve<Size<In>, Size<Out>> for Size<T> {
75 fn maybe_resolve(self, context: Size<In>) -> Size<Out> {
77 Size { width: self.width.maybe_resolve(context.width), height: self.height.maybe_resolve(context.height) }
78 }
79}
80
81impl ResolveOrZero<Option<f32>, f32> for LengthPercentage {
82 fn resolve_or_zero(self, context: Option<f32>) -> f32 {
84 self.maybe_resolve(context).unwrap_or(0.0)
85 }
86}
87
88impl ResolveOrZero<Option<f32>, f32> for LengthPercentageAuto {
89 fn resolve_or_zero(self, context: Option<f32>) -> f32 {
91 self.maybe_resolve(context).unwrap_or(0.0)
92 }
93}
94
95impl ResolveOrZero<Option<f32>, f32> for Dimension {
96 fn resolve_or_zero(self, context: Option<f32>) -> f32 {
98 self.maybe_resolve(context).unwrap_or(0.0)
99 }
100}
101
102impl<In, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Size<Out>> for Size<T> {
104 fn resolve_or_zero(self, context: Size<In>) -> Size<Out> {
106 Size { width: self.width.resolve_or_zero(context.width), height: self.height.resolve_or_zero(context.height) }
107 }
108}
109
110impl<In: Copy, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Rect<Out>> for Rect<T> {
112 fn resolve_or_zero(self, context: Size<In>) -> Rect<Out> {
114 Rect {
115 left: self.left.resolve_or_zero(context.width),
116 right: self.right.resolve_or_zero(context.width),
117 top: self.top.resolve_or_zero(context.height),
118 bottom: self.bottom.resolve_or_zero(context.height),
119 }
120 }
121}
122
123impl<Out: TaffyZero, T: ResolveOrZero<Option<f32>, Out>> ResolveOrZero<Option<f32>, Rect<Out>> for Rect<T> {
125 fn resolve_or_zero(self, context: Option<f32>) -> Rect<Out> {
127 Rect {
128 left: self.left.resolve_or_zero(context),
129 right: self.right.resolve_or_zero(context),
130 top: self.top.resolve_or_zero(context),
131 bottom: self.bottom.resolve_or_zero(context),
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::{MaybeResolve, ResolveOrZero};
139 use crate::style_helpers::TaffyZero;
140 use core::fmt::Debug;
141
142 fn mr_case<Lhs, Rhs, Out>(input: Lhs, context: Rhs, expected: Out)
144 where
145 Lhs: MaybeResolve<Rhs, Out>,
146 Out: PartialEq + Debug,
147 {
148 assert_eq!(input.maybe_resolve(context), expected);
149 }
150
151 fn roz_case<Lhs, Rhs, Out>(input: Lhs, context: Rhs, expected: Out)
153 where
154 Lhs: ResolveOrZero<Rhs, Out>,
155 Out: PartialEq + Debug + TaffyZero,
156 {
157 assert_eq!(input.resolve_or_zero(context), expected);
158 }
159
160 mod maybe_resolve_dimension {
161 use super::mr_case;
162 use crate::style::Dimension;
163
164 #[test]
168 fn resolve_auto() {
169 mr_case(Dimension::Auto, None, None);
170 mr_case(Dimension::Auto, Some(5.0), None);
171 mr_case(Dimension::Auto, Some(-5.0), None);
172 mr_case(Dimension::Auto, Some(0.), None);
173 }
174
175 #[test]
180 fn resolve_length() {
181 mr_case(Dimension::Length(1.0), None, Some(1.0));
182 mr_case(Dimension::Length(1.0), Some(5.0), Some(1.0));
183 mr_case(Dimension::Length(1.0), Some(-5.0), Some(1.0));
184 mr_case(Dimension::Length(1.0), Some(0.), Some(1.0));
185 }
186
187 #[test]
193 fn resolve_percent() {
194 mr_case(Dimension::Percent(1.0), None, None);
195 mr_case(Dimension::Percent(1.0), Some(5.0), Some(5.0));
196 mr_case(Dimension::Percent(1.0), Some(-5.0), Some(-5.0));
197 mr_case(Dimension::Percent(1.0), Some(50.0), Some(50.0));
198 }
199 }
200
201 mod maybe_resolve_size_dimension {
202 use super::mr_case;
203 use crate::geometry::Size;
204 use crate::style::Dimension;
205
206 #[test]
210 fn maybe_resolve_auto() {
211 mr_case(Size::<Dimension>::auto(), Size::NONE, Size::NONE);
212 mr_case(Size::<Dimension>::auto(), Size::new(5.0, 5.0), Size::NONE);
213 mr_case(Size::<Dimension>::auto(), Size::new(-5.0, -5.0), Size::NONE);
214 mr_case(Size::<Dimension>::auto(), Size::new(0.0, 0.0), Size::NONE);
215 }
216
217 #[test]
222 fn maybe_resolve_length() {
223 mr_case(Size::from_lengths(5.0, 5.0), Size::NONE, Size::new(5.0, 5.0));
224 mr_case(Size::from_lengths(5.0, 5.0), Size::new(5.0, 5.0), Size::new(5.0, 5.0));
225 mr_case(Size::from_lengths(5.0, 5.0), Size::new(-5.0, -5.0), Size::new(5.0, 5.0));
226 mr_case(Size::from_lengths(5.0, 5.0), Size::new(0.0, 0.0), Size::new(5.0, 5.0));
227 }
228
229 #[test]
235 fn maybe_resolve_percent() {
236 mr_case(Size::from_percent(5.0, 5.0), Size::NONE, Size::NONE);
237 mr_case(Size::from_percent(5.0, 5.0), Size::new(5.0, 5.0), Size::new(25.0, 25.0));
238 mr_case(Size::from_percent(5.0, 5.0), Size::new(-5.0, -5.0), Size::new(-25.0, -25.0));
239 mr_case(Size::from_percent(5.0, 5.0), Size::new(0.0, 0.0), Size::new(0.0, 0.0));
240 }
241 }
242
243 mod resolve_or_zero_dimension_to_option_f32 {
244 use super::roz_case;
245 use crate::style::Dimension;
246
247 #[test]
248 fn resolve_or_zero_auto() {
249 roz_case(Dimension::Auto, None, 0.0);
250 roz_case(Dimension::Auto, Some(5.0), 0.0);
251 roz_case(Dimension::Auto, Some(-5.0), 0.0);
252 roz_case(Dimension::Auto, Some(0.0), 0.0);
253 }
254 #[test]
255 fn resolve_or_zero_length() {
256 roz_case(Dimension::Length(5.0), None, 5.0);
257 roz_case(Dimension::Length(5.0), Some(5.0), 5.0);
258 roz_case(Dimension::Length(5.0), Some(-5.0), 5.0);
259 roz_case(Dimension::Length(5.0), Some(0.0), 5.0);
260 }
261 #[test]
262 fn resolve_or_zero_percent() {
263 roz_case(Dimension::Percent(5.0), None, 0.0);
264 roz_case(Dimension::Percent(5.0), Some(5.0), 25.0);
265 roz_case(Dimension::Percent(5.0), Some(-5.0), -25.0);
266 roz_case(Dimension::Percent(5.0), Some(0.0), 0.0);
267 }
268 }
269
270 mod resolve_or_zero_rect_dimension_to_rect {
271 use super::roz_case;
272 use crate::geometry::{Rect, Size};
273 use crate::style::Dimension;
274
275 #[test]
276 fn resolve_or_zero_auto() {
277 roz_case(Rect::<Dimension>::auto(), Size::NONE, Rect::zero());
278 roz_case(Rect::<Dimension>::auto(), Size::new(5.0, 5.0), Rect::zero());
279 roz_case(Rect::<Dimension>::auto(), Size::new(-5.0, -5.0), Rect::zero());
280 roz_case(Rect::<Dimension>::auto(), Size::new(0.0, 0.0), Rect::zero());
281 }
282
283 #[test]
284 fn resolve_or_zero_length() {
285 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), Size::NONE, Rect::new(5.0, 5.0, 5.0, 5.0));
286 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), Size::new(5.0, 5.0), Rect::new(5.0, 5.0, 5.0, 5.0));
287 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), Size::new(-5.0, -5.0), Rect::new(5.0, 5.0, 5.0, 5.0));
288 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), Size::new(0.0, 0.0), Rect::new(5.0, 5.0, 5.0, 5.0));
289 }
290
291 #[test]
292 fn resolve_or_zero_percent() {
293 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Size::NONE, Rect::zero());
294 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Size::new(5.0, 5.0), Rect::new(25.0, 25.0, 25.0, 25.0));
295 roz_case(
296 Rect::from_percent(5.0, 5.0, 5.0, 5.0),
297 Size::new(-5.0, -5.0),
298 Rect::new(-25.0, -25.0, -25.0, -25.0),
299 );
300 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Size::new(0.0, 0.0), Rect::zero());
301 }
302 }
303
304 mod resolve_or_zero_rect_dimension_to_rect_f32_via_option {
305 use super::roz_case;
306 use crate::geometry::Rect;
307 use crate::style::Dimension;
308
309 #[test]
310 fn resolve_or_zero_auto() {
311 roz_case(Rect::<Dimension>::auto(), None, Rect::zero());
312 roz_case(Rect::<Dimension>::auto(), Some(5.0), Rect::zero());
313 roz_case(Rect::<Dimension>::auto(), Some(-5.0), Rect::zero());
314 roz_case(Rect::<Dimension>::auto(), Some(0.0), Rect::zero());
315 }
316
317 #[test]
318 fn resolve_or_zero_length() {
319 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), None, Rect::new(5.0, 5.0, 5.0, 5.0));
320 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), Some(5.0), Rect::new(5.0, 5.0, 5.0, 5.0));
321 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), Some(-5.0), Rect::new(5.0, 5.0, 5.0, 5.0));
322 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), Some(0.0), Rect::new(5.0, 5.0, 5.0, 5.0));
323 }
324
325 #[test]
326 fn resolve_or_zero_percent() {
327 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), None, Rect::zero());
328 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Some(5.0), Rect::new(25.0, 25.0, 25.0, 25.0));
329 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Some(-5.0), Rect::new(-25.0, -25.0, -25.0, -25.0));
330 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Some(0.0), Rect::zero());
331 }
332 }
333}