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