Skip to main content

use_dimensions/
lib.rs

1#![forbid(unsafe_code)]
2//! Primitive media dimension helpers.
3//!
4//! These helpers keep width and height calculations explicit and validated.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use use_dimensions::{Dimensions, is_landscape, pixel_area};
10//!
11//! let size = Dimensions::new(1920, 1080).unwrap();
12//!
13//! assert_eq!(size.area(), 2_073_600);
14//! assert!(size.is_landscape());
15//! assert_eq!(pixel_area(1920, 1080).unwrap(), 2_073_600);
16//! assert!(is_landscape(1920, 1080).unwrap());
17//! ```
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct Dimensions {
21    width: u32,
22    height: u32,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum DimensionsError {
27    InvalidWidth,
28    InvalidHeight,
29}
30
31fn validate_dimensions(width: u32, height: u32) -> Result<(u32, u32), DimensionsError> {
32    if width == 0 {
33        return Err(DimensionsError::InvalidWidth);
34    }
35
36    if height == 0 {
37        return Err(DimensionsError::InvalidHeight);
38    }
39
40    Ok((width, height))
41}
42
43impl Dimensions {
44    pub fn new(width: u32, height: u32) -> Result<Self, DimensionsError> {
45        let (width, height) = validate_dimensions(width, height)?;
46        Ok(Self { width, height })
47    }
48
49    #[must_use]
50    pub fn width(&self) -> u32 {
51        self.width
52    }
53
54    #[must_use]
55    pub fn height(&self) -> u32 {
56        self.height
57    }
58
59    #[must_use]
60    pub fn area(&self) -> u64 {
61        u64::from(self.width) * u64::from(self.height)
62    }
63
64    #[must_use]
65    pub fn is_landscape(&self) -> bool {
66        self.width > self.height
67    }
68
69    #[must_use]
70    pub fn is_portrait(&self) -> bool {
71        self.height > self.width
72    }
73
74    #[must_use]
75    pub fn is_square(&self) -> bool {
76        self.width == self.height
77    }
78}
79
80pub fn pixel_area(width: u32, height: u32) -> Result<u64, DimensionsError> {
81    Ok(Dimensions::new(width, height)?.area())
82}
83
84pub fn is_landscape(width: u32, height: u32) -> Result<bool, DimensionsError> {
85    Ok(Dimensions::new(width, height)?.is_landscape())
86}
87
88pub fn is_portrait(width: u32, height: u32) -> Result<bool, DimensionsError> {
89    Ok(Dimensions::new(width, height)?.is_portrait())
90}
91
92pub fn is_square(width: u32, height: u32) -> Result<bool, DimensionsError> {
93    Ok(Dimensions::new(width, height)?.is_square())
94}
95
96#[cfg(test)]
97mod tests {
98    use super::{Dimensions, DimensionsError, is_landscape, is_portrait, is_square, pixel_area};
99
100    #[test]
101    fn validates_dimensions_and_area() {
102        let dimensions = Dimensions::new(1920, 1080).unwrap();
103
104        assert_eq!(dimensions.width(), 1920);
105        assert_eq!(dimensions.height(), 1080);
106        assert_eq!(dimensions.area(), 2_073_600);
107        assert!(dimensions.is_landscape());
108        assert!(!dimensions.is_portrait());
109        assert!(!dimensions.is_square());
110        assert_eq!(pixel_area(1920, 1080).unwrap(), 2_073_600);
111        assert!(is_landscape(1920, 1080).unwrap());
112    }
113
114    #[test]
115    fn detects_portrait_and_square_sizes() {
116        assert!(is_portrait(1080, 1920).unwrap());
117        assert!(is_square(512, 512).unwrap());
118        assert!(!is_square(640, 480).unwrap());
119    }
120
121    #[test]
122    fn rejects_invalid_dimensions() {
123        assert_eq!(Dimensions::new(0, 1080), Err(DimensionsError::InvalidWidth));
124        assert_eq!(
125            Dimensions::new(1920, 0),
126            Err(DimensionsError::InvalidHeight)
127        );
128        assert_eq!(pixel_area(0, 1), Err(DimensionsError::InvalidWidth));
129    }
130}