1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4#[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#[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#[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#[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#[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#[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#[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#[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}