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_plain_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_plain_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::CONTENT_TAG => None,
66 CompactLength::LENGTH_TAG => Some(self.0.value()),
67 CompactLength::PERCENT_TAG => context.map(|dim| dim * self.0.value()),
68 #[cfg(feature = "calc")]
69 _ if self.0.is_plain_calc() => context.map(|dim| calc(self.0.calc_value(), dim)),
70 _ if self.0.is_sizing_keyword() => None,
73 _ => unreachable!(),
74 }
75 }
76}
77
78impl<T: MaybeResolve<Option<f32>, Option<f32>>> MaybeResolve<f32, Option<f32>> for T {
81 fn maybe_resolve(self, context: f32, calc: impl Fn(*const (), f32) -> f32) -> Option<f32> {
84 self.maybe_resolve(Some(context), calc)
85 }
86}
87
88impl<In, Out, T: MaybeResolve<In, Out>> MaybeResolve<Size<In>, Size<Out>> for Size<T> {
90 #[inline(always)]
92 fn maybe_resolve(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Size<Out> {
93 Size {
94 width: self.width.maybe_resolve(context.width, &calc),
95 height: self.height.maybe_resolve(context.height, &calc),
96 }
97 }
98}
99
100impl ResolveOrZero<Option<f32>, f32> for LengthPercentage {
101 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
103 self.maybe_resolve(context, calc).unwrap_or(0.0)
104 }
105}
106
107impl ResolveOrZero<Option<f32>, f32> for LengthPercentageAuto {
108 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
110 self.maybe_resolve(context, calc).unwrap_or(0.0)
111 }
112}
113
114impl ResolveOrZero<Option<f32>, f32> for Dimension {
115 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> f32 {
117 self.maybe_resolve(context, calc).unwrap_or(0.0)
118 }
119}
120
121impl<In, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Size<Out>> for Size<T> {
123 fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Size<Out> {
125 Size {
126 width: self.width.resolve_or_zero(context.width, &calc),
127 height: self.height.resolve_or_zero(context.height, &calc),
128 }
129 }
130}
131
132impl<In: Copy, Out: TaffyZero, T: ResolveOrZero<In, Out>> ResolveOrZero<Size<In>, Rect<Out>> for Rect<T> {
134 fn resolve_or_zero(self, context: Size<In>, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
136 Rect {
137 left: self.left.resolve_or_zero(context.width, &calc),
138 right: self.right.resolve_or_zero(context.width, &calc),
139 top: self.top.resolve_or_zero(context.height, &calc),
140 bottom: self.bottom.resolve_or_zero(context.height, &calc),
141 }
142 }
143}
144
145impl<Out: TaffyZero, T: ResolveOrZero<Option<f32>, Out>> ResolveOrZero<Option<f32>, Rect<Out>> for Rect<T> {
147 #[inline(always)]
149 fn resolve_or_zero(self, context: Option<f32>, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
150 Rect {
151 left: self.left.resolve_or_zero(context, &calc),
152 right: self.right.resolve_or_zero(context, &calc),
153 top: self.top.resolve_or_zero(context, &calc),
154 bottom: self.bottom.resolve_or_zero(context, &calc),
155 }
156 }
157}
158
159impl<Out: TaffyZero, T: ResolveOrZero<f32, Out>> ResolveOrZero<f32, Rect<Out>> for Rect<T> {
161 fn resolve_or_zero(self, context: f32, calc: impl Fn(*const (), f32) -> f32) -> Rect<Out> {
163 Rect {
164 left: self.left.resolve_or_zero(context, &calc),
165 right: self.right.resolve_or_zero(context, &calc),
166 top: self.top.resolve_or_zero(context, &calc),
167 bottom: self.bottom.resolve_or_zero(context, &calc),
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::{MaybeResolve, ResolveOrZero};
175 use crate::style_helpers::TaffyZero;
176 use core::fmt::Debug;
177
178 fn mr_case<Lhs, Rhs, Out>(input: Lhs, context: Rhs, expected: Out)
180 where
181 Lhs: MaybeResolve<Rhs, Out>,
182 Out: PartialEq + Debug,
183 {
184 assert_eq!(input.maybe_resolve(context, |_, _| 42.42), expected);
185 }
186
187 fn roz_case<Lhs, Rhs, Out>(input: Lhs, context: Rhs, expected: Out)
189 where
190 Lhs: ResolveOrZero<Rhs, Out>,
191 Out: PartialEq + Debug + TaffyZero,
192 {
193 assert_eq!(input.resolve_or_zero(context, |_, _| 42.42), expected);
194 }
195
196 mod maybe_resolve_dimension {
197 use super::mr_case;
198 use crate::style::Dimension;
199 use crate::style_helpers::*;
200
201 #[test]
205 fn resolve_auto() {
206 mr_case(Dimension::AUTO, None, None);
207 mr_case(Dimension::AUTO, Some(5.0), None);
208 mr_case(Dimension::AUTO, Some(-5.0), None);
209 mr_case(Dimension::AUTO, Some(0.), None);
210 }
211
212 #[test]
217 fn resolve_length() {
218 mr_case(Dimension::from_length(1.0), None, Some(1.0));
219 mr_case(Dimension::from_length(1.0), Some(5.0), Some(1.0));
220 mr_case(Dimension::from_length(1.0), Some(-5.0), Some(1.0));
221 mr_case(Dimension::from_length(1.0), Some(0.), Some(1.0));
222 }
223
224 #[test]
230 fn resolve_percent() {
231 mr_case(Dimension::from_percent(1.0), None, None);
232 mr_case(Dimension::from_percent(1.0), Some(5.0), Some(5.0));
233 mr_case(Dimension::from_percent(1.0), Some(-5.0), Some(-5.0));
234 mr_case(Dimension::from_percent(1.0), Some(50.0), Some(50.0));
235 }
236 }
237
238 mod maybe_resolve_size_dimension {
239 use super::mr_case;
240 use crate::geometry::Size;
241 use crate::style::Dimension;
242
243 #[test]
247 fn maybe_resolve_auto() {
248 mr_case(Size::<Dimension>::auto(), Size::NONE, Size::NONE);
249 mr_case(Size::<Dimension>::auto(), Size::new(5.0, 5.0), Size::NONE);
250 mr_case(Size::<Dimension>::auto(), Size::new(-5.0, -5.0), Size::NONE);
251 mr_case(Size::<Dimension>::auto(), Size::new(0.0, 0.0), Size::NONE);
252 }
253
254 #[test]
259 fn maybe_resolve_length() {
260 mr_case(Size::from_lengths(5.0, 5.0), Size::NONE, Size::new(5.0, 5.0));
261 mr_case(Size::from_lengths(5.0, 5.0), Size::new(5.0, 5.0), Size::new(5.0, 5.0));
262 mr_case(Size::from_lengths(5.0, 5.0), Size::new(-5.0, -5.0), Size::new(5.0, 5.0));
263 mr_case(Size::from_lengths(5.0, 5.0), Size::new(0.0, 0.0), Size::new(5.0, 5.0));
264 }
265
266 #[test]
272 fn maybe_resolve_percent() {
273 mr_case(Size::from_percent(5.0, 5.0), Size::NONE, Size::NONE);
274 mr_case(Size::from_percent(5.0, 5.0), Size::new(5.0, 5.0), Size::new(25.0, 25.0));
275 mr_case(Size::from_percent(5.0, 5.0), Size::new(-5.0, -5.0), Size::new(-25.0, -25.0));
276 mr_case(Size::from_percent(5.0, 5.0), Size::new(0.0, 0.0), Size::new(0.0, 0.0));
277 }
278 }
279
280 mod resolve_or_zero_dimension_to_option_f32 {
281 use super::roz_case;
282 use crate::style::Dimension;
283 use crate::style_helpers::*;
284
285 #[test]
286 fn resolve_or_zero_auto() {
287 roz_case(Dimension::AUTO, None, 0.0);
288 roz_case(Dimension::AUTO, Some(5.0), 0.0);
289 roz_case(Dimension::AUTO, Some(-5.0), 0.0);
290 roz_case(Dimension::AUTO, Some(0.0), 0.0);
291 }
292 #[test]
293 fn resolve_or_zero_length() {
294 roz_case(Dimension::from_length(5.0), None, 5.0);
295 roz_case(Dimension::from_length(5.0), Some(5.0), 5.0);
296 roz_case(Dimension::from_length(5.0), Some(-5.0), 5.0);
297 roz_case(Dimension::from_length(5.0), Some(0.0), 5.0);
298 }
299 #[test]
300 fn resolve_or_zero_percent() {
301 roz_case(Dimension::from_percent(5.0), None, 0.0);
302 roz_case(Dimension::from_percent(5.0), Some(5.0), 25.0);
303 roz_case(Dimension::from_percent(5.0), Some(-5.0), -25.0);
304 roz_case(Dimension::from_percent(5.0), Some(0.0), 0.0);
305 }
306 }
307
308 mod resolve_or_zero_rect_dimension_to_rect {
309 use super::roz_case;
310 use crate::geometry::{Rect, Size};
311 use crate::style::Dimension;
312
313 #[test]
314 fn resolve_or_zero_auto() {
315 roz_case(Rect::<Dimension>::auto(), Size::NONE, Rect::zero());
316 roz_case(Rect::<Dimension>::auto(), Size::new(5.0, 5.0), Rect::zero());
317 roz_case(Rect::<Dimension>::auto(), Size::new(-5.0, -5.0), Rect::zero());
318 roz_case(Rect::<Dimension>::auto(), Size::new(0.0, 0.0), Rect::zero());
319 }
320
321 #[test]
322 fn resolve_or_zero_length() {
323 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));
324 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));
325 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));
326 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));
327 }
328
329 #[test]
330 fn resolve_or_zero_percent() {
331 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Size::NONE, Rect::zero());
332 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));
333 roz_case(
334 Rect::from_percent(5.0, 5.0, 5.0, 5.0),
335 Size::new(-5.0, -5.0),
336 Rect::new(-25.0, -25.0, -25.0, -25.0),
337 );
338 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Size::new(0.0, 0.0), Rect::zero());
339 }
340 }
341
342 mod resolve_or_zero_rect_dimension_to_rect_f32_via_option {
343 use super::roz_case;
344 use crate::geometry::Rect;
345 use crate::style::Dimension;
346
347 #[test]
348 fn resolve_or_zero_auto() {
349 roz_case(Rect::<Dimension>::auto(), None, Rect::zero());
350 roz_case(Rect::<Dimension>::auto(), Some(5.0), Rect::zero());
351 roz_case(Rect::<Dimension>::auto(), Some(-5.0), Rect::zero());
352 roz_case(Rect::<Dimension>::auto(), Some(0.0), Rect::zero());
353 }
354
355 #[test]
356 fn resolve_or_zero_length() {
357 roz_case(Rect::from_length(5.0, 5.0, 5.0, 5.0), None, Rect::new(5.0, 5.0, 5.0, 5.0));
358 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));
359 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));
360 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));
361 }
362
363 #[test]
364 fn resolve_or_zero_percent() {
365 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), None, Rect::zero());
366 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));
367 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));
368 roz_case(Rect::from_percent(5.0, 5.0, 5.0, 5.0), Some(0.0), Rect::zero());
369 }
370 }
371}