⎇ bd0150f5fca7eadd62553898d3b064ccc3c54897
src/style/dimension.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
Validate tag when deserializing values (#805)
1//! Style types for representing lengths / sizes
2use super::CompactLength;
3use crate::geometry::Rect;
4use crate::style_helpers::{FromLength, FromPercent, TaffyAuto, TaffyZero};
5
6/// A unit of linear measurement
7///
8/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
9#[derive(Copy, Clone, PartialEq, Debug)]
10#[cfg_attr(feature = "serde", derive(Serialize))]
11pub struct LengthPercentage(pub(crate) CompactLength);
12impl TaffyZero for LengthPercentage {
13 const ZERO: Self = Self(CompactLength::ZERO);
14}
15impl FromLength for LengthPercentage {
16 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
17 Self::length(value.into())
18 }
19}
20impl FromPercent for LengthPercentage {
21 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
22 Self::percent(value.into())
23 }
24}
25impl LengthPercentage {
26 /// An absolute length in some abstract units. Users of Taffy may define what they correspond
27 /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
28 #[inline(always)]
29 pub const fn length(val: f32) -> Self {
30 Self(CompactLength::length(val))
31 }
32
33 /// A percentage length relative to the size of the containing block.
34 ///
35 /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
36 #[inline(always)]
37 pub const fn percent(val: f32) -> Self {
38 Self(CompactLength::percent(val))
39 }
40
41 /// A `calc()` value. The value passed here is treated as an opaque handle to
42 /// the actual calc representation and may be a pointer, index, etc.
43 ///
44 /// The low 3 bits are used as a tag value and will be returned as 0.
45 #[inline(always)]
46 pub fn calc(ptr: *const ()) -> Self {
47 Self(CompactLength::calc(ptr))
48 }
49
50 /// Create a LengthPercentage from a raw `CompactLength`.
51 /// # Safety
52 /// CompactLength must represent a valid variant for LengthPercentage
53 #[allow(unsafe_code)]
54 pub unsafe fn from_raw(val: CompactLength) -> Self {
55 Self(val)
56 }
57
58 /// Get the underlying `CompactLength` representation of the value
59 pub fn into_raw(self) -> CompactLength {
60 self.0
61 }
62}
63
64#[cfg(feature = "serde")]
65impl<'de> serde::Deserialize<'de> for LengthPercentage {
66 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67 where
68 D: serde::Deserializer<'de>,
69 {
70 let inner = CompactLength::deserialize(deserializer)?;
71 // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
72 if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG) {
73 Ok(Self(inner))
74 } else {
75 Err(serde::de::Error::custom("Invalid tag"))
76 }
77 }
78}
79
80/// A unit of linear measurement
81///
82/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
83#[derive(Copy, Clone, PartialEq, Debug)]
84#[cfg_attr(feature = "serde", derive(Serialize))]
85pub struct LengthPercentageAuto(pub(crate) CompactLength);
86impl TaffyZero for LengthPercentageAuto {
87 const ZERO: Self = Self(CompactLength::ZERO);
88}
89impl TaffyAuto for LengthPercentageAuto {
90 const AUTO: Self = Self(CompactLength::AUTO);
91}
92impl FromLength for LengthPercentageAuto {
93 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
94 Self::length(value.into())
95 }
96}
97impl FromPercent for LengthPercentageAuto {
98 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
99 Self::percent(value.into())
100 }
101}
102impl From<LengthPercentage> for LengthPercentageAuto {
103 fn from(input: LengthPercentage) -> Self {
104 Self(input.0)
105 }
106}
107
108impl LengthPercentageAuto {
109 /// An absolute length in some abstract units. Users of Taffy may define what they correspond
110 /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
111 #[inline(always)]
112 pub const fn length(val: f32) -> Self {
113 Self(CompactLength::length(val))
114 }
115
116 /// A percentage length relative to the size of the containing block.
117 ///
118 /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
119 #[inline(always)]
120 pub const fn percent(val: f32) -> Self {
121 Self(CompactLength::percent(val))
122 }
123
124 /// The dimension should be automatically computed according to algorithm-specific rules
125 /// regarding the default size of boxes.
126 #[inline(always)]
127 pub const fn auto() -> Self {
128 Self(CompactLength::auto())
129 }
130
131 /// A `calc()` value. The value passed here is treated as an opaque handle to
132 /// the actual calc representation and may be a pointer, index, etc.
133 ///
134 /// The low 3 bits are used as a tag value and will be returned as 0.
135 #[inline]
136 pub fn calc(ptr: *const ()) -> Self {
137 Self(CompactLength::calc(ptr))
138 }
139
140 /// Create a LengthPercentageAuto from a raw `CompactLength`.
141 /// # Safety
142 /// CompactLength must represent a valid variant for LengthPercentageAuto
143 #[allow(unsafe_code)]
144 pub unsafe fn from_raw(val: CompactLength) -> Self {
145 Self(val)
146 }
147
148 /// Get the underlying `CompactLength` representation of the value
149 pub fn into_raw(self) -> CompactLength {
150 self.0
151 }
152
153 /// Returns:
154 /// - Some(length) for Length variants
155 /// - Some(resolved) using the provided context for Percent variants
156 /// - None for Auto variants
157 #[inline(always)]
158 pub fn resolve_to_option(self, context: f32, calc_resolver: impl Fn(*const (), f32) -> f32) -> Option<f32> {
159 match self.0.tag() {
160 CompactLength::LENGTH_TAG => Some(self.0.value()),
161 CompactLength::PERCENT_TAG => Some(context * self.0.value()),
162 CompactLength::AUTO_TAG => None,
163 _ if self.0.is_calc() => Some(calc_resolver(self.0.calc_value(), context)),
164 _ => unreachable!("LengthPercentageAuto values cannot be constructed with other tags"),
165 }
166 }
167
168 /// Returns true if value is LengthPercentageAuto::Auto
169 #[inline(always)]
170 pub fn is_auto(self) -> bool {
171 self.0.is_auto()
172 }
173}
174
175#[cfg(feature = "serde")]
176impl<'de> serde::Deserialize<'de> for LengthPercentageAuto {
177 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178 where
179 D: serde::Deserializer<'de>,
180 {
181 let inner = CompactLength::deserialize(deserializer)?;
182 // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
183 if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG | CompactLength::AUTO_TAG) {
184 Ok(Self(inner))
185 } else {
186 Err(serde::de::Error::custom("Invalid tag"))
187 }
188 }
189}
190
191/// A unit of linear measurement
192///
193/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
194#[derive(Copy, Clone, PartialEq, Debug)]
195#[cfg_attr(feature = "serde", derive(Serialize))]
196pub struct Dimension(pub(crate) CompactLength);
197impl TaffyZero for Dimension {
198 const ZERO: Self = Self(CompactLength::ZERO);
199}
200impl TaffyAuto for Dimension {
201 const AUTO: Self = Self(CompactLength::AUTO);
202}
203impl FromLength for Dimension {
204 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
205 Self::length(value.into())
206 }
207}
208impl FromPercent for Dimension {
209 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
210 Self::percent(value.into())
211 }
212}
213impl From<LengthPercentage> for Dimension {
214 fn from(input: LengthPercentage) -> Self {
215 Self(input.0)
216 }
217}
218impl From<LengthPercentageAuto> for Dimension {
219 fn from(input: LengthPercentageAuto) -> Self {
220 Self(input.0)
221 }
222}
223
224impl Dimension {
225 /// An absolute length in some abstract units. Users of Taffy may define what they correspond
226 /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
227 #[inline(always)]
228 pub const fn length(val: f32) -> Self {
229 Self(CompactLength::length(val))
230 }
231
232 /// A percentage length relative to the size of the containing block.
233 ///
234 /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
235 #[inline(always)]
236 pub const fn percent(val: f32) -> Self {
237 Self(CompactLength::percent(val))
238 }
239
240 /// The dimension should be automatically computed according to algorithm-specific rules
241 /// regarding the default size of boxes.
242 #[inline(always)]
243 pub const fn auto() -> Self {
244 Self(CompactLength::auto())
245 }
246
247 /// A `calc()` value. The value passed here is treated as an opaque handle to
248 /// the actual calc representation and may be a pointer, index, etc.
249 ///
250 /// The low 3 bits are used as a tag value and will be returned as 0.
251 #[inline]
252 pub fn calc(ptr: *const ()) -> Self {
253 Self(CompactLength::calc(ptr))
254 }
255
256 /// Create a LengthPercentageAuto from a raw `CompactLength`.
257 /// # Safety
258 /// CompactLength must represent a valid variant for LengthPercentageAuto
259 #[allow(unsafe_code)]
260 pub unsafe fn from_raw(val: CompactLength) -> Self {
261 Self(val)
262 }
263
264 /// Get the underlying `CompactLength` representation of the value
265 pub fn into_raw(self) -> CompactLength {
266 self.0
267 }
268
269 /// Get Length value if value is Length variant
270 #[cfg(feature = "grid")]
271 pub fn into_option(self) -> Option<f32> {
272 match self.0.tag() {
273 CompactLength::LENGTH_TAG => Some(self.0.value()),
274 _ => None,
275 }
276 }
277 /// Returns true if value is Auto
278 #[inline(always)]
279 pub fn is_auto(self) -> bool {
280 self.0.is_auto()
281 }
282
283 /// Get the raw `CompactLength` tag
284 pub fn tag(self) -> usize {
285 self.0.tag()
286 }
287
288 /// Get the raw `CompactLength` value for non-calc variants that have a numeric parameter
289 pub fn value(self) -> f32 {
290 self.0.value()
291 }
292}
293
294#[cfg(feature = "serde")]
295impl<'de> serde::Deserialize<'de> for Dimension {
296 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297 where
298 D: serde::Deserializer<'de>,
299 {
300 let inner = CompactLength::deserialize(deserializer)?;
301 // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
302 if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG | CompactLength::AUTO_TAG) {
303 Ok(Self(inner))
304 } else {
305 Err(serde::de::Error::custom("Invalid tag"))
306 }
307 }
308}
309
310impl Rect<Dimension> {
311 /// Create a new Rect with length values
312 #[must_use]
313 pub const fn from_length(start: f32, end: f32, top: f32, bottom: f32) -> Self {
314 Rect {
315 left: Dimension(CompactLength::length(start)),
316 right: Dimension(CompactLength::length(end)),
317 top: Dimension(CompactLength::length(top)),
318 bottom: Dimension(CompactLength::length(bottom)),
319 }
320 }
321
322 /// Create a new Rect with percentage values
323 #[must_use]
324 pub const fn from_percent(start: f32, end: f32, top: f32, bottom: f32) -> Self {
325 Rect {
326 left: Dimension(CompactLength::percent(start)),
327 right: Dimension(CompactLength::percent(end)),
328 top: Dimension(CompactLength::percent(top)),
329 bottom: Dimension(CompactLength::percent(bottom)),
330 }
331 }
332}