1use crate::geometry::{Rect, Size};
4use crate::style::{Dimension, LengthPercentage, LengthPercentageAuto};
5use crate::style_helpers::TaffyZero;
6use crate::CompactLength;
7
8pub trait MaybeResolve<In, Out> {
14 fn maybe_resolve(self, context: In, calc: impl Fn(*const (), f32) -> f32) -> Out;
16}
17
18pub trait ResolveOrZero<TContext, TOutput: TaffyZero> {
24 fn resolve_or_zero(self, context: TContext, calc: impl Fn(*const (), f32) -> f32) -> TOutput;
26}
27
28impl MaybeResolve<Option<f32>, Option<f32>> for LengthPercentage {
29 fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
32 match self.0.tag() {
33 CompactLength::LENGTH_TAG => Some(self.0.value()),
34 CompactLength::PERCENT_TAG => context.map(|dim| dim * self.0.value()),
35 #[cfg(feature = "calc")]
36 _ if self.0.is_calc() => context.map(|dim| calc(self.0.calc_value(), dim)),
37 _ => unreachable!(),
38 }
39 }
40}
41
42impl MaybeResolve<Option<f32>, Option<f32>> for LengthPercentageAuto {
43 fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
46 match self.0.tag() {
47 CompactLength::AUTO_TAG => None,
48 CompactLength::LENGTH_TAG => Some(self.0.value()),
49 CompactLength::PERCENT_TAG => context.map(|dim| dim * self.0.value()),
50 #[cfg(feature = "calc")]
51 _ if self.0.is_calc() => context.map(|dim| calc(self.0.calc_value(), dim)),
52 _ => unreachable!(),
53 }
54 }
55}
56
57impl MaybeResolve<Option<f32>, Option<f32>> for Dimension {
58 fn maybe_resolve(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
62 match self.0.tag() {
63 CompactLength::AUTO_TAG => None,
64 CompactLength::LENGTH_TAG => Some(self.0.value()),
65 CompactLength::PERCENT_TAG => context.map(|dim| dim * self.0.value()),
66 #[cfg(feature = "calc")]
67 _ if self.0.is_calc() => context.map(|dim| calc(self.0.calc_value(), dim)),
68 _ => unreachable!(),
69 }
70 }
71}
72
73impl<T: MaybeResolve<Option<f32>, Option<f32>>> MaybeResolve<f32, Option<f32>> for T {
76 fn maybe_resolve(self, context: f32, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
79 self.maybe_resolve(Some(context), calc)
80 }
81}
82
83impl<In, Out, T: MaybeResolve<In, Out>> MaybeResolve<Size<In>, Size<Out>> for Size<T> {
85 fn maybe_resolve(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Size<Out> {
87 Size {
88 width: self.width.maybe_resolve(context.width, &calc),
89 height: self.height.maybe_resolve(context.height, &calc),
90 }
91 }
92}
93
94impl ResolveOrZero<Option<f32>, f32> for LengthPercentage {
95 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
97 self.maybe_resolve(context, calc).unwrap_or(0.0)
98 }
99}
100
101impl ResolveOrZero<Option<f32>, f32> for LengthPercentageAuto {
102 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
104 self.maybe_resolve(context, calc).unwrap_or(0.0)
105 }
106}
107
108impl ResolveOrZero<Option<f32>, f32> for Dimension {
109 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
111 self.maybe_resolve(context, calc).unwrap_or(0.0)
112 }
113}
114
115impl<In, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Size<Out>> for Size<T> {
117 fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Size<Out> {
119 Size {
120 width: self.width.resolve_or_zero(context.width, &calc),
121 height: self.height.resolve_or_zero(context.height, &calc),
122 }
123 }
124}
125
126impl<In: Copy, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Rect<Out>> for Rect<T> {
128 fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
130 Rect {
131 left: self.left.resolve_or_zero(context.width, &calc),
132 right: self.right.resolve_or_zero(context.width, &calc),
133 top: self.top.resolve_or_zero(context.height, &calc),
134 bottom: self.bottom.resolve_or_zero(context.height, &calc),
135 }
136 }
137}
138
139impl<Out: TaffyZero, T: ResolveOrZero<Option<f32>, Out>> ResolveOrZero<Option<f32>, Rect<Out>> for Rect<T> {
141 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
143 Rect {
144 left: self.left.resolve_or_zero(context, &calc),
145 right: self.right.resolve_or_zero(context, &calc),
146 top: self.top.resolve_or_zero(context, &calc),
147 bottom: self.bottom.resolve_or_zero(context, &calc),
148 }
149 }
150}
151
152impl<Out: TaffyZero, T: ResolveOrZero<f32, Out>> ResolveOrZero<f32, Rect<Out>> for Rect<T> {
154 fn resolve_or_zero(self, context: f32, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
156 Rect {
157 left: self.left.resolve_or_zero(context, &calc),
158 right: self.right.resolve_or_zero(context, &calc),
159 top: self.top.resolve_or_zero(context, &calc),
160 bottom: self.bottom.resolve_or_zero(context, &calc),
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::{MaybeResolve, ResolveOrZero};
168 use crate::style_helpers::TaffyZero;
169 use core::fmt::Debug;
170
171 fn mr_case<Lhs, Rhs, Out>(input: Lhs, context: Rhs, expected: Out)
173 where
174 Lhs: MaybeResolve<Rhs, Out>,
175 Out: PartialEq + Debug,
176 {
177 assert_eq!(input.maybe_resolve(context, |_, _| 42.42), expected);
178 }
179
180 fn roz_case<Lhs, Rhs, Out>(input: Lhs, context: Rhs, expected: Out)
182 where
183 Lhs: ResolveOrZero<Rhs, Out>,
184 Out: PartialEq + Debug + TaffyZero,
185 {
186 assert_eq!(input.resolve_or_zero(context, |_, _| 42.42), expected);
187 }
188
189 mod maybe_resolve_dimension {
190 use super::mr_case;
191 use crate::style::Dimension;
192 use crate::style_helpers::*;
193
194 #[test]
198 fn resolve_auto() {
199 mr_case(Dimension::AUTO, None, None);
200 mr_case(Dimension::AUTO, Some(5.0), None);
201 mr_case(Dimension::AUTO, Some(-5.0), None);
202 mr_case(Dimension::AUTO, Some(0.), None);
203 }
204
205 #[test]
210 fn resolve_length() {
211 mr_case(Dimension::from_length(1.0), None, Some(1.0));
212 mr_case(Dimension::from_length(1.0), Some(5.0), Some(1.0));
213 mr_case(Dimension::from_length(1.0), Some(-5.0), Some(1.0));
214 mr_case(Dimension::from_length(1.0), Some(0.), Some(1.0));
215 }
216
217 #[test]
223 fn resolve_percent() {
224 mr_case(Dimension::from_percent(1.0), None, None);
225 mr_case(Dimension::from_percent(1.0), Some(5.0), Some(5.0));
226 mr_case(Dimension::from_percent(1.0), Some(-5.0), Some(-5.0));
227 mr_case(Dimension::from_percent(1.0), Some(50.0), Some(50.0));
228 }
229 }
230
231 mod maybe_resolve_size_dimension {
232 use super::mr_case;
233 use crate::geometry::Size;
234 use crate::style::Dimension;
235
236 #[test]
240 fn maybe_resolve_auto() {
241 mr_case(Size::<Dimension>::auto(), Size::NONE, Size::NONE);
242 mr_case(Size::<Dimension>::auto(), Size::new(5.0, 5.0), Size::NONE);
243 mr_case(Size::<Dimension>::auto(), Size::new(-5.0, -5.0), Size::NONE);
244 mr_case(Size::<Dimension>::auto(), Size::new(0.0, 0.0), Size::NONE);
245 }
246
247 #[test]
252 fn maybe_resolve_length() {
253 mr_case(Size::from_lengths(5.0, 5.0), Size::NONE, Size::new(5.0, 5.0));
254 mr_case(Size::from_lengths(5.0, 5.0), Size::new(5.0, 5.0), Size::new(5.0, 5.0));
255 mr_case(Size::from_lengths(5.0, 5.0), Size::new(-5.0, -5.0), Size::new(5.0, 5.0));
256 mr_case(Size::from_lengths(5.0, 5.0), Size::new(0.0, 0.0), Size::new(5.0, 5.0));
257 }
258
259 #[test]
265 fn maybe_resolve_percent() {
266 mr_case(Size::from_percent(5.0, 5.0), Size::NONE, Size::NONE);
267 mr_case(Size::from_percent(5.0, 5.0), Size::new(5.0, 5.0), Size::new(25.0, 25.0));
268 mr_case(Size::from_percent(5.0, 5.0), Size::new(-5.0, -5.0), Size::new(-25.0, -25.0));
269 mr_case(Size::from_percent(5.0, 5.0), Size::new(0.0, 0.0), Size::new(0.0, 0.0));
270 }
271 }
272
273 mod resolve_or_zero_dimension_to_option_f32 {
274 use super::roz_case;
275 use crate::style::Dimension;
276 use crate::style_helpers::*;
277
278 #[test]
279 fn resolve_or_zero_auto() {
280 roz_case(Dimension::AUTO, None, 0.0);
281 roz_case(Dimension::AUTO, Some(5.0), 0.0);
282 roz_case(Dimension::AUTO, Some(-5.0), 0.0);
283 roz_case(Dimension::AUTO, Some(0.0), 0.0);
284 }
285 #[test]
286 fn resolve_or_zero_length() {
287 roz_case(Dimension::from_length(5.0), None, 5.0);
288 roz_case(Dimension::from_length(5.0), Some(5.0), 5.0);
289 roz_case(Dimension::from_length(5.0), Some(-5.0), 5.0);
290 roz_case(Dimension::from_length(5.0), Some(0.0), 5.0);
291 }
292 #[test]
293 fn resolve_or_zero_percent() {
294 roz_case(Dimension::from_percent(5.0), None, 0.0);
295 roz_case(Dimension::from_percent(5.0), Some(5.0), 25.0);
296 roz_case(Dimension::from_percent(5.0), Some(-5.0), -25.0);
297 roz_case(Dimension::from_percent(5.0), Some(0.0), 0.0);
298 }
299 }
300
301 mod resolve_or_zero_rect_dimension_to_rect {
302 use super::roz_case;
303 use crate::geometry::{Rect, Size};
304 use crate::style::Dimension;
305
306 #[test]
307 fn resolve_or_zero_auto() {
308 roz_case(Rect::<Dimension>::auto(), Size::NONE, Rect::zero());
309 roz_case(Rect::<Dimension>::auto(), Size::new(5.0, 5.0), Rect::zero());
310 roz_case(Rect::<Dimension>::auto(), Size::new(-5.0, -5.0), Rect::zero());
311 roz_case(Rect::<Dimension>::auto(), Size::new(0.0, 0.0), Rect::zero());
312 }
313
314 #[test]
315 fn resolve_or_zero_length() {
316 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));
317 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));
318 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));
319 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));
320 }
321
322 #[test]
323 fn resolve_or_zero_percent() {
324 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Size::NONE, Rect::zero());
325 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));
326 roz_case(
327 Rect::from_percent(5.0, 5.0, 5.0, 5.0),
328 Size::new(-5.0, -5.0),
329 Rect::new(-25.0, -25.0, -25.0, -25.0),
330 );
331 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Size::new(0.0, 0.0), Rect::zero());
332 }
333 }
334
335 mod resolve_or_zero_rect_dimension_to_rect_f32_via_option {
336 use super::roz_case;
337 use crate::geometry::Rect;
338 use crate::style::Dimension;
339
340 #[test]
341 fn resolve_or_zero_auto() {
342 roz_case(Rect::<Dimension>::auto(), None, Rect::zero());
343 roz_case(Rect::<Dimension>::auto(), Some(5.0), Rect::zero());
344 roz_case(Rect::<Dimension>::auto(), Some(-5.0), Rect::zero());
345 roz_case(Rect::<Dimension>::auto(), Some(0.0), Rect::zero());
346 }
347
348 #[test]
349 fn resolve_or_zero_length() {
350 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), None, Rect::new(5.0, 5.0, 5.0, 5.0));
351 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));
352 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));
353 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));
354 }
355
356 #[test]
357 fn resolve_or_zero_percent() {
358 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), None, Rect::zero());
359 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));
360 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));
361 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Some(0.0), Rect::zero());
362 }
363 }
364}