1use std::{fmt, ops};
34
35use crate::{
36 margin, padding, position, size,
37 unit::{Length, Percent},
38};
39use paste::paste;
40
41#[derive(Debug, PartialOrd, PartialEq, Clone)]
46pub enum Calc {
47 Length(Box<Length>),
49 Percent(Percent),
50 CssVariable(String),
51
52 Sum(Box<Calc>, Box<Calc>),
56
57 Sub(Box<Calc>, Box<Calc>),
59
60 Mul(Box<Calc>, f32),
62
63 Mul2(f32, Box<Calc>),
66
67 Div(Box<Calc>, f32),
69 }
75
76impl From<Length> for Calc {
77 fn from(source: Length) -> Self {
78 Calc::Length(Box::new(source))
79 }
80}
81
82impl TryFrom<padding::Length> for Calc {
83 type Error = &'static str;
84
85 fn try_from(value: padding::Length) -> Result<Self, Self::Error> {
86 match value {
87 padding::Length::Length(len) => Ok(len.into()),
88 padding::Length::Percent(per) => Ok(per.into()),
89 padding::Length::Inherit => Err("Calc cannot accept inherit as value"),
90 }
91 }
92}
93
94impl TryFrom<margin::Length> for Calc {
95 type Error = &'static str;
96
97 fn try_from(value: margin::Length) -> Result<Self, Self::Error> {
98 match value {
99 margin::Length::Length(len) => Ok(len.into()),
100 margin::Length::Percent(per) => Ok(per.into()),
101 _ => Err("Calc cannot accept values such as inherit, auto ..etc"),
102 }
103 }
104}
105
106impl TryFrom<size::Length> for Calc {
107 type Error = &'static str;
108
109 fn try_from(value: size::Length) -> Result<Self, Self::Error> {
110 match value {
111 size::Length::Length(len) => Ok(len.into()),
112 size::Length::Percent(per) => Ok(per.into()),
113 _ => Err("Calc cannot accept values such as inherit, auto ..etc"),
114 }
115 }
116}
117
118impl TryFrom<position::PostionLength> for Calc {
119 type Error = &'static str;
120
121 fn try_from(value: position::PostionLength) -> Result<Self, Self::Error> {
122 match value {
123 position::PostionLength::Length(len) => Ok(len.into()),
124 position::PostionLength::Percent(per) => Ok(per.into()),
125 _ => Err("Calc cannot accept values such as inherit, auto ..etc"),
126 }
127 }
128}
129
130impl From<String> for Calc {
131 fn from(source: String) -> Self {
132 Calc::CssVariable(source)
133 }
134}
135
136impl From<&str> for Calc {
137 fn from(source: &str) -> Self {
138 Calc::CssVariable(source.to_string())
139 }
140}
141
142impl fmt::Display for Calc {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 fn fmt_calc(calc: &Calc) -> String {
145 match calc {
146 Calc::Length(val) => val.to_string(),
147 Calc::Percent(val) => val.to_string(),
148 Calc::CssVariable(val) => format!("var({})", val.to_string()),
149 Calc::Sum(c, val) => format!("({} + {})", fmt_calc(c), fmt_calc(val)),
150 Calc::Sub(c, val) => format!("({} - {})", fmt_calc(c), fmt_calc(val)),
151 Calc::Mul(c, val) => format!("({} * {})", fmt_calc(c), val),
152 Calc::Mul2(val, c) => format!("({} * {})", val, fmt_calc(c)),
153 Calc::Div(c, val) => format!("({} / {})", fmt_calc(c), val),
154 }
155 }
156 match self {
157 Self::Length(val) => write!(f, "{}", val),
158 Self::Percent(val) => write!(f, "{}", val),
159 Self::CssVariable(val) => write!(f, "calc(var({}))", val),
160 _ => write!(f, "calc{}", fmt_calc(self)),
161 }
162 }
163}
164
165macro_rules! ops_impl {
166 ($($op: ident<$($t: ty),*> for $target: ty => { $e: expr }),* $(,)?) => {
167 $(
168 $(
169 paste! {
170 impl ops::$op<$t> for $target {
171 type Output = Calc;
172
173 fn [<$op:snake>](self, rhs: $t) -> Calc {
174 $e(self, rhs)
175 }
176 }
177 }
178 )*
179 )*
180 };
181
182 ($($op: ident<$($t: ty),*> and Optional for $target: ty => { $e: expr }),* $(,)?) => {
183 $(
184 $(
185 paste! {
186 impl ops::$op<$t> for $target {
187 type Output = Calc;
188
189 fn [<$op:snake>](self, rhs: $t) -> Calc {
190 $e(self, rhs)
191 }
192 }
193 }
194
195 )*
196
197 impl<T> ops::$op<Option<T>> for $target
198 where
199 $target: ops::$op<T, Output = Calc>,
200 {
201 type Output = Calc;
202
203 paste! {
204 fn [<$op:snake>](self, rhs: Option<T>) -> Self::Output {
205 if let Some(length) = rhs {
206 self.[<$op:snake>](length)
207 } else {
208 self.into()
209 }
210 }
211 }
212 }
213 )*
214 };
215}
216
217ops_impl!(
219 Add<Calc, Percent, Length> and Optional for Calc => { |c, rhs| Self::Sum(Box::new(c), Box::new(Calc::from(rhs))) },
220 Sub<Calc, Percent, Length> and Optional for Calc => { |c, rhs| Self::Sub(Box::new(c), Box::new(Calc::from(rhs))) },
221 Mul<f32, i32, i16, i8, u32, u16, u8, isize, usize> and Optional for Calc => { |c, val| Self::Mul(Box::new(c), val as f32) },
222 Div<f32, i32, i16, i8, u32, u16, u8, isize, usize> and Optional for Calc => { |c, val| Self::Div(Box::new(c), val as f32) },
223);
224
225ops_impl!(
226 Mul<Calc> for f32 => { |val, c| Calc::Mul2(val, Box::new(c)) },
227 Mul<Calc> for i32 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
228 Mul<Calc> for i16 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
229 Mul<Calc> for i8 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
230 Mul<Calc> for u32 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
231 Mul<Calc> for u16 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
232 Mul<Calc> for u8 => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
233 Mul<Calc> for isize => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
234 Mul<Calc> for usize => { |val, c| Calc::Mul2(val as f32, Box::new(c)) },
235
236 Mul<Length> for f32 => { |val, length| Calc::Mul2(val, Box::new(Calc::from(length))) },
237 Mul<Length> for i32 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
238 Mul<Length> for i16 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
239 Mul<Length> for i8 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
240 Mul<Length> for u32 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
241 Mul<Length> for u16 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
242 Mul<Length> for u8 => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
243 Mul<Length> for isize => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
244 Mul<Length> for usize => { |val, length| Calc::Mul2(val as f32, Box::new(Calc::from(length))) },
245
246 Mul<Percent> for f32 => { |val, pct| Calc::Mul2(val, Box::new(Calc::from(pct))) },
247 Mul<Percent> for i32 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
248 Mul<Percent> for i16 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
249 Mul<Percent> for i8 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
250 Mul<Percent> for u32 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
251 Mul<Percent> for u16 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
252 Mul<Percent> for u8 => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
253 Mul<Percent> for isize => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
254 Mul<Percent> for usize => { |val, pct| Calc::Mul2(val as f32, Box::new(Calc::from(pct))) },
255
256 Add<Calc, Percent, Length> for Length => { |length, rhs| Calc::from(length).add(rhs) },
257 Sub<Calc, Percent, Length> for Length => { |length, rhs| Calc::from(length).sub(rhs) },
258 Mul<f32, i32, i16, i8, u32, u16, u8, isize, usize> for Length => { |length, val| Calc::from(length).mul(val as f32) },
259 Div<f32, i32, i16, i8, u32, u16, u8, isize, usize> for Length => { |length, val| Calc::from(length).div(val as f32) },
260);
261
262#[derive(Clone, Debug, PartialEq, Display, From)]
263pub enum CalcArgument {
264 Length(Length),
265 Percent(Percent),
266 CssVariable(String),
267}
268
269impl From<&str> for CalcArgument {
270 fn from(source: &str) -> Self {
271 CalcArgument::CssVariable(source.to_string())
272 }
273}
274
275pub fn calc(value: impl Into<CalcArgument>) -> Calc {
276 match value.into() {
277 CalcArgument::Length(len) => Calc::Length(Box::new(len)),
278 CalcArgument::Percent(per) => Calc::Percent(per),
279 CalcArgument::CssVariable(var) => Calc::CssVariable(var),
280 }
281}