Skip to main content

pixel8/
dim.rs

1//! Drawing-dimension conversion.
2//!
3//! `core` provides `TryFrom<u16>` for `NonZeroU16` but no `TryFrom<i16>`, so a
4//! computed signed size would not convert. This sealed trait accepts the integer
5//! types a cart actually holds — a literal, a computed coordinate difference, or
6//! an explicit value — and validates each to a strictly positive size.
7
8use core::num::NonZeroU16;
9
10mod sealed {
11    pub trait Sealed {}
12}
13
14/// A drawing dimension (width, height, radius, tile count).
15///
16/// Implemented for `i16`, `u16`, `i32`, `u32` and `NonZeroU16`, so a draw call
17/// accepts a literal, a value computed from positions, or an already-validated
18/// size. The wider `i32`/`u32` impls keep unsuffixed literals (which default to
19/// `i32`) and computed `i32`/`u32` differences usable without a suffix.
20pub trait Dim: sealed::Sealed {
21    /// The dimension as a strictly positive value, or `None` if it was zero,
22    /// negative, or larger than `u16::MAX`.
23    fn to_nonzero(self) -> Option<NonZeroU16>;
24}
25
26impl sealed::Sealed for i16 {}
27impl Dim for i16 {
28    fn to_nonzero(self) -> Option<NonZeroU16> {
29        u16::try_from(self).ok().and_then(NonZeroU16::new)
30    }
31}
32
33impl sealed::Sealed for u16 {}
34impl Dim for u16 {
35    fn to_nonzero(self) -> Option<NonZeroU16> {
36        NonZeroU16::new(self)
37    }
38}
39
40impl sealed::Sealed for i32 {}
41impl Dim for i32 {
42    fn to_nonzero(self) -> Option<NonZeroU16> {
43        u16::try_from(self).ok().and_then(NonZeroU16::new)
44    }
45}
46
47impl sealed::Sealed for u32 {}
48impl Dim for u32 {
49    fn to_nonzero(self) -> Option<NonZeroU16> {
50        u16::try_from(self).ok().and_then(NonZeroU16::new)
51    }
52}
53
54impl sealed::Sealed for NonZeroU16 {}
55impl Dim for NonZeroU16 {
56    fn to_nonzero(self) -> Option<NonZeroU16> {
57        Some(self)
58    }
59}
60
61/// A draw call was given a size that was not strictly positive (zero or
62/// negative). The call drew nothing.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct ZeroSize;
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use core::num::NonZeroU16;
70
71    #[test]
72    fn i16_conversions() {
73        assert_eq!(5i16.to_nonzero(), NonZeroU16::new(5));
74        assert_eq!(0i16.to_nonzero(), None);
75        assert_eq!((-3i16).to_nonzero(), None);
76    }
77
78    #[test]
79    fn u16_conversions() {
80        assert_eq!(5u16.to_nonzero(), NonZeroU16::new(5));
81        assert_eq!(0u16.to_nonzero(), None);
82    }
83
84    #[test]
85    fn i32_conversions() {
86        assert_eq!(5i32.to_nonzero(), NonZeroU16::new(5));
87        assert_eq!(0i32.to_nonzero(), None);
88        assert_eq!((-3i32).to_nonzero(), None);
89        // Larger than u16::MAX does not fit.
90        assert_eq!(70000i32.to_nonzero(), None);
91    }
92
93    #[test]
94    fn nonzero_passes_through() {
95        let n = NonZeroU16::new(7).unwrap();
96        assert_eq!(n.to_nonzero(), Some(n));
97    }
98}