Skip to main content

use_spacing/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4/// A named or numeric spacing step.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
6pub enum SpacingStep {
7    None,
8    Xs,
9    Sm,
10    Md,
11    Lg,
12    Xl,
13    Xxl,
14    Custom(u32),
15}
16
17impl SpacingStep {
18    pub fn units(self) -> u32 {
19        match self {
20            Self::None => 0,
21            Self::Xs => 1,
22            Self::Sm => 2,
23            Self::Md => 3,
24            Self::Lg => 4,
25            Self::Xl => 6,
26            Self::Xxl => 8,
27            Self::Custom(value) => value,
28        }
29    }
30}
31
32/// A spacing value stored as scale units.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
34pub struct SpacingValue(u32);
35
36impl SpacingValue {
37    pub fn new(units: u32) -> Self {
38        Self(units)
39    }
40
41    pub fn from_step(step: SpacingStep) -> Self {
42        Self(step.units())
43    }
44
45    pub fn units(self) -> u32 {
46        self.0
47    }
48}
49
50/// A numeric spacing scale.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
52pub struct SpacingScale {
53    unit: u32,
54}
55
56impl SpacingScale {
57    pub fn new(unit: u32) -> Self {
58        Self { unit }
59    }
60
61    pub fn unit(self) -> u32 {
62        self.unit
63    }
64
65    pub fn resolve(self, value: SpacingValue) -> u32 {
66        self.unit.saturating_mul(value.units())
67    }
68}
69
70/// Inner edge spacing.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72pub struct Inset {
73    pub top: SpacingValue,
74    pub right: SpacingValue,
75    pub bottom: SpacingValue,
76    pub left: SpacingValue,
77}
78
79impl Inset {
80    pub fn all(value: SpacingValue) -> Self {
81        Self {
82            top: value,
83            right: value,
84            bottom: value,
85            left: value,
86        }
87    }
88
89    pub fn symmetric(vertical: SpacingValue, horizontal: SpacingValue) -> Self {
90        Self {
91            top: vertical,
92            right: horizontal,
93            bottom: vertical,
94            left: horizontal,
95        }
96    }
97}
98
99/// Outer edge spacing.
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101pub struct Outset {
102    pub top: SpacingValue,
103    pub right: SpacingValue,
104    pub bottom: SpacingValue,
105    pub left: SpacingValue,
106}
107
108impl Outset {
109    pub fn all(value: SpacingValue) -> Self {
110        Self {
111            top: value,
112            right: value,
113            bottom: value,
114            left: value,
115        }
116    }
117}
118
119/// Gap between repeated items.
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
121pub struct Gap(SpacingValue);
122
123impl Gap {
124    pub fn new(value: SpacingValue) -> Self {
125        Self(value)
126    }
127
128    pub fn value(self) -> SpacingValue {
129        self.0
130    }
131}
132
133/// Padding metadata.
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
135pub struct Padding(Inset);
136
137impl Padding {
138    pub fn new(inset: Inset) -> Self {
139        Self(inset)
140    }
141
142    pub fn inset(self) -> Inset {
143        self.0
144    }
145}
146
147/// Margin metadata.
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
149pub struct Margin(Outset);
150
151impl Margin {
152    pub fn new(outset: Outset) -> Self {
153        Self(outset)
154    }
155
156    pub fn outset(self) -> Outset {
157        self.0
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::{Gap, Inset, Margin, Outset, Padding, SpacingScale, SpacingStep, SpacingValue};
164
165    #[test]
166    fn resolves_named_spacing_values() {
167        let scale = SpacingScale::new(4);
168        let medium = SpacingValue::from_step(SpacingStep::Md);
169
170        assert_eq!(SpacingStep::None.units(), 0);
171        assert_eq!(medium.units(), 3);
172        assert_eq!(scale.resolve(medium), 12);
173        assert_eq!(scale.resolve(SpacingValue::from_step(SpacingStep::Xxl)), 32);
174    }
175
176    #[test]
177    fn creates_box_spacing_primitives() {
178        let small = SpacingValue::from_step(SpacingStep::Sm);
179        let large = SpacingValue::from_step(SpacingStep::Lg);
180        let inset = Inset::symmetric(small, large);
181        let padding = Padding::new(inset);
182        let margin = Margin::new(Outset::all(small));
183        let gap = Gap::new(large);
184
185        assert_eq!(padding.inset().top, small);
186        assert_eq!(padding.inset().right, large);
187        assert_eq!(margin.outset().left, small);
188        assert_eq!(gap.value(), large);
189    }
190}