1use crate::css::Css;
8use crate::data_type::LengthPercentage;
9
10#[derive(Clone, Debug, PartialEq)]
20pub struct Padding {
21 pub trbl: [LengthPercentage; 4],
23}
24
25impl<T: Into<LengthPercentage>> From<T> for Padding {
26 fn from(v: T) -> Self {
27 let v = v.into();
28 Self {
29 trbl: [v.clone(), v.clone(), v.clone(), v],
30 }
31 }
32}
33
34impl<A, B> From<(A, B)> for Padding
36where
37 A: Into<LengthPercentage>,
38 B: Into<LengthPercentage>,
39{
40 fn from((y, x): (A, B)) -> Self {
41 let y = y.into();
42 let x = x.into();
43 Self {
44 trbl: [y.clone(), x.clone(), y, x],
45 }
46 }
47}
48
49impl<A, B, C> From<(A, B, C)> for Padding
51where
52 A: Into<LengthPercentage>,
53 B: Into<LengthPercentage>,
54 C: Into<LengthPercentage>,
55{
56 fn from((t, x, b): (A, B, C)) -> Self {
57 let x = x.into();
58 Self {
59 trbl: [t.into(), x.clone(), b.into(), x],
60 }
61 }
62}
63
64impl<A, B, C, D> From<(A, B, C, D)> for Padding
66where
67 A: Into<LengthPercentage>,
68 B: Into<LengthPercentage>,
69 C: Into<LengthPercentage>,
70 D: Into<LengthPercentage>,
71{
72 fn from((t, r, b, l): (A, B, C, D)) -> Self {
73 Self {
74 trbl: [t.into(), r.into(), b.into(), l.into()],
75 }
76 }
77}
78
79#[derive(Clone, Debug, PartialEq)]
82pub enum MarginValue {
83 LengthPercentage(LengthPercentage),
85 Auto,
87}
88
89impl crate::to_css::ToCss for MarginValue {
90 fn to_css(&self, dest: &mut dyn core::fmt::Write) -> core::fmt::Result {
91 match self {
92 MarginValue::LengthPercentage(lp) => lp.to_css(dest),
93 MarginValue::Auto => dest.write_str("auto"),
94 }
95 }
96}
97
98impl From<LengthPercentage> for MarginValue {
99 fn from(v: LengthPercentage) -> Self {
100 Self::LengthPercentage(v)
101 }
102}
103
104impl From<crate::data_type::Length> for MarginValue {
105 fn from(v: crate::data_type::Length) -> Self {
106 Self::LengthPercentage(v.into())
107 }
108}
109
110impl From<crate::data_type::Percentage> for MarginValue {
111 fn from(v: crate::data_type::Percentage) -> Self {
112 Self::LengthPercentage(v.into())
113 }
114}
115
116#[derive(Clone, Debug, PartialEq)]
119pub struct Margin {
120 pub trbl: [MarginValue; 4],
122}
123
124impl<T: Into<MarginValue>> From<T> for Margin {
125 fn from(v: T) -> Self {
126 let v = v.into();
127 Self {
128 trbl: [v.clone(), v.clone(), v.clone(), v],
129 }
130 }
131}
132
133impl<A, B> From<(A, B)> for Margin
134where
135 A: Into<MarginValue>,
136 B: Into<MarginValue>,
137{
138 fn from((y, x): (A, B)) -> Self {
139 let y = y.into();
140 let x = x.into();
141 Self {
142 trbl: [y.clone(), x.clone(), y, x],
143 }
144 }
145}
146
147impl<A, B, C> From<(A, B, C)> for Margin
148where
149 A: Into<MarginValue>,
150 B: Into<MarginValue>,
151 C: Into<MarginValue>,
152{
153 fn from((t, x, b): (A, B, C)) -> Self {
154 let x = x.into();
155 Self {
156 trbl: [t.into(), x.clone(), b.into(), x],
157 }
158 }
159}
160
161impl<A, B, C, D> From<(A, B, C, D)> for Margin
162where
163 A: Into<MarginValue>,
164 B: Into<MarginValue>,
165 C: Into<MarginValue>,
166 D: Into<MarginValue>,
167{
168 fn from((t, r, b, l): (A, B, C, D)) -> Self {
169 Self {
170 trbl: [t.into(), r.into(), b.into(), l.into()],
171 }
172 }
173}
174
175impl Css {
176 pub fn padding(self, v: impl Into<Padding>) -> Self {
182 let Padding { trbl: [t, r, b, l] } = v.into();
183 self.padding_top(t)
184 .padding_right(r)
185 .padding_bottom(b)
186 .padding_left(l)
187 }
188
189 pub fn margin(self, v: impl Into<Margin>) -> Self {
193 let Margin { trbl: [t, r, b, l] } = v.into();
194 self.push("margin-top", t)
195 .push("margin-right", r)
196 .push("margin-bottom", b)
197 .push("margin-left", l)
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use crate::ext::*;
204 use crate::shorthand::padding_margin::MarginValue;
205 use crate::Css;
206
207 #[test]
208 fn padding_single_value_expands_to_four() {
209 let s = Css::new().padding(px(8));
210 assert_eq!(
211 s.to_string(),
212 "padding-top: 8px; padding-right: 8px; padding-bottom: 8px; padding-left: 8px;"
213 );
214 }
215
216 #[test]
217 fn padding_two_value_y_x() {
218 let s = Css::new().padding((px(8), px(16)));
219 assert_eq!(
220 s.to_string(),
221 "padding-top: 8px; padding-right: 16px; padding-bottom: 8px; padding-left: 16px;"
222 );
223 }
224
225 #[test]
226 fn padding_three_value_t_x_b() {
227 let s = Css::new().padding((px(8), px(16), px(4)));
228 assert_eq!(
229 s.to_string(),
230 "padding-top: 8px; padding-right: 16px; padding-bottom: 4px; padding-left: 16px;"
231 );
232 }
233
234 #[test]
235 fn padding_four_value_trbl() {
236 let s = Css::new().padding((px(2), px(4), px(6), px(8)));
237 assert_eq!(
238 s.to_string(),
239 "padding-top: 2px; padding-right: 4px; padding-bottom: 6px; padding-left: 8px;"
240 );
241 }
242
243 #[test]
244 fn padding_then_single_side_override() {
245 let s = Css::new().padding(px(8)).padding_top(px(0));
246 assert_eq!(
247 s.to_string(),
248 "padding-right: 8px; padding-bottom: 8px; padding-left: 8px; padding-top: 0px;"
249 );
250 }
251
252 #[test]
253 fn padding_percentages() {
254 let s = Css::new().padding(50.percent());
255 assert_eq!(
256 s.to_string(),
257 "padding-top: 50%; padding-right: 50%; padding-bottom: 50%; padding-left: 50%;"
258 );
259 }
260
261 #[test]
262 fn margin_single_value() {
263 let s = Css::new().margin(px(8));
264 assert_eq!(
265 s.to_string(),
266 "margin-top: 8px; margin-right: 8px; margin-bottom: 8px; margin-left: 8px;"
267 );
268 }
269
270 #[test]
271 fn margin_auto_centers() {
272 let s = Css::new().margin((px(0), MarginValue::Auto));
273 assert_eq!(
274 s.to_string(),
275 "margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto;"
276 );
277 }
278
279 #[test]
280 fn margin_four_value_with_negative() {
281 let s = Css::new().margin((px(-4), px(0), px(4), MarginValue::Auto));
282 assert_eq!(
283 s.to_string(),
284 "margin-top: -4px; margin-right: 0px; margin-bottom: 4px; margin-left: auto;"
285 );
286 }
287
288 #[test]
289 fn margin_three_value_t_x_b() {
290 let s = Css::new().margin((px(2), px(4), px(6)));
291 assert_eq!(
292 s.to_string(),
293 "margin-top: 2px; margin-right: 4px; margin-bottom: 6px; margin-left: 4px;"
294 );
295 }
296
297 #[test]
298 fn margin_value_from_length_percentage() {
299 let v: MarginValue = px(4).into();
300 let v2: MarginValue = 25.percent().into();
301 let v3: MarginValue =
302 crate::data_type::LengthPercentage::Length(crate::data_type::Length::Px(8.0)).into();
303 assert!(matches!(v, MarginValue::LengthPercentage(_)));
304 assert!(matches!(v2, MarginValue::LengthPercentage(_)));
305 assert!(matches!(v3, MarginValue::LengthPercentage(_)));
306 }
307}