1use rustc_hash::FxHashMap;
2use swc_atoms::{atom, Atom};
3use swc_common::Span;
4use swc_css_ast::*;
5
6use super::Compressor;
7use crate::compressor::math::{is_calc_function_name, transform_calc_value_into_component_value};
8
9#[derive(Debug, Clone)]
12enum CalcOperatorNode {
13 Sum(Vec<CalcNode>),
14 Product(Vec<CalcNode>),
15 Negate(CalcNode),
16 Invert(CalcNode),
17}
18
19#[derive(Debug, Clone)]
20enum CalcNode {
21 Number(Number),
22 Dimension(Dimension),
23 Percentage(Percentage),
24 Constant(Ident),
25 Function(Function),
26 OperatorNode(Box<CalcOperatorNode>),
27}
28
29fn get_precision(n: f64) -> u32 {
30 let n_as_str = n.to_string();
31 n_as_str
32 .find('.')
33 .map_or(0, |sep| (n_as_str.len() - sep) as u32 - 1)
34}
35
36fn collect_calc_sum_into_calc_node(calc_sum: &CalcSum) -> CalcNode {
39 let mut is_negated = false;
40 let mut operands: Vec<CalcNode> = Vec::new();
41 for node in &calc_sum.expressions {
42 match &node {
43 CalcProductOrOperator::Product(calc_product) => {
44 let mut node = collect_calc_product_into_calc_node(calc_product);
45 if is_negated {
46 node = CalcNode::OperatorNode(Box::new(CalcOperatorNode::Negate(node)));
47 }
48 operands.push(node);
49 }
50 CalcProductOrOperator::Operator(CalcOperator {
51 value: CalcOperatorType::Sub,
52 ..
53 }) => {
54 is_negated = true;
55 }
56 CalcProductOrOperator::Operator(CalcOperator {
57 value: CalcOperatorType::Add,
58 ..
59 }) => {
60 is_negated = false;
61 }
62 _ => {}
63 }
64 }
65
66 if operands.len() == 1 {
67 operands[0].clone()
68 } else {
69 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Sum(operands)))
70 }
71}
72
73fn collect_calc_product_into_calc_node(calc_product: &CalcProduct) -> CalcNode {
74 let mut is_inverted = false;
75 let mut operands: Vec<CalcNode> = Vec::new();
76 for node in &calc_product.expressions {
77 match &node {
78 CalcValueOrOperator::Value(calc_value) => {
79 let mut node = collect_calc_value_into_calc_node(calc_value);
80 if is_inverted {
81 node = CalcNode::OperatorNode(Box::new(CalcOperatorNode::Invert(node)));
82 }
83 operands.push(node);
84 }
85 CalcValueOrOperator::Operator(CalcOperator {
86 value: CalcOperatorType::Div,
87 ..
88 }) => {
89 is_inverted = true;
90 }
91 CalcValueOrOperator::Operator(CalcOperator {
92 value: CalcOperatorType::Mul,
93 ..
94 }) => {
95 is_inverted = false;
96 }
97 _ => {}
98 }
99 }
100
101 if operands.len() == 1 {
102 operands[0].clone()
103 } else {
104 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Product(operands)))
105 }
106}
107
108fn collect_calc_value_into_calc_node(calc_value: &CalcValue) -> CalcNode {
109 match calc_value {
110 CalcValue::Number(n) => CalcNode::Number(n.clone()),
111 CalcValue::Dimension(d) => CalcNode::Dimension(d.clone()),
112 CalcValue::Percentage(p) => CalcNode::Percentage(p.clone()),
113 CalcValue::Constant(c) => CalcNode::Constant(c.clone()),
114 CalcValue::Function(f @ Function { name, value, .. })
115 if is_calc_function_name(name) && value.len() == 1 =>
116 {
117 match &value[0] {
118 ComponentValue::CalcSum(calc_sum) => collect_calc_sum_into_calc_node(calc_sum),
119 _ => CalcNode::Function(f.clone()),
120 }
121 }
122 CalcValue::Function(f) => CalcNode::Function(f.clone()),
123 CalcValue::Sum(calc_sum) => collect_calc_sum_into_calc_node(calc_sum),
124 }
125}
126
127fn simplify_calc_node(calc_node: &CalcNode) -> CalcNode {
135 match calc_node {
136 CalcNode::Number(_)
137 | CalcNode::Dimension(_)
138 | CalcNode::Percentage(_)
139 | CalcNode::Constant(_)
140 | CalcNode::Function(_) => calc_node.clone(),
141 CalcNode::OperatorNode(op) => simplify_calc_operator_node(op),
142 }
143}
144
145fn simplify_calc_operator_node(calc_operator_node: &CalcOperatorNode) -> CalcNode {
146 match calc_operator_node {
147 CalcOperatorNode::Invert(inverted_calc_node) => {
148 simplify_calc_operator_node_invert(inverted_calc_node)
149 }
150 CalcOperatorNode::Negate(negated_calc_node) => {
151 simplify_calc_operator_node_negate(negated_calc_node)
152 }
153 CalcOperatorNode::Product(nodes) => simplify_calc_operator_node_product(nodes),
154 CalcOperatorNode::Sum(nodes) => simplify_calc_operator_node_sum(nodes),
155 }
156}
157
158fn simplify_calc_operator_node_invert(calc_node: &CalcNode) -> CalcNode {
162 let calc_node = simplify_calc_node(calc_node);
163 match &calc_node {
164 CalcNode::Number(n) => {
165 if let Some(result) = try_to_divide_values(1.0, n.value) {
168 CalcNode::Number(Number {
169 value: result,
170 span: n.span,
171 raw: n.raw.clone(),
172 })
173 } else {
174 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Invert(calc_node.clone())))
175 }
176 }
177 CalcNode::OperatorNode(op) => {
178 match &**op {
179 CalcOperatorNode::Invert(node) => node.clone(),
181 CalcOperatorNode::Product(children) => {
182 if let Some(inverted_fraction) =
184 try_to_invert_if_children_are_fraction(children)
185 {
186 inverted_fraction
187 } else {
188 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Invert(
189 calc_node.clone(),
190 )))
191 }
192 }
193 _ => CalcNode::OperatorNode(Box::new(CalcOperatorNode::Invert(calc_node.clone()))),
194 }
195 }
196 _ => CalcNode::OperatorNode(Box::new(CalcOperatorNode::Invert(calc_node.clone()))),
197 }
198}
199
200fn simplify_calc_operator_node_negate(calc_node: &CalcNode) -> CalcNode {
202 try_to_switch_sign_of_nodes(&[simplify_calc_node(calc_node)]).unwrap_or_else(|| {
203 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Negate(calc_node.clone())))
204 })
205}
206
207fn simplify_calc_operator_node_product(nodes: &[CalcNode]) -> CalcNode {
209 let mut nodes = nodes.to_vec();
210
211 let mut idx = 0;
214 while idx < nodes.len() {
215 nodes[idx] = simplify_calc_node(&nodes[idx]);
216 if let Some(children) = get_children_if_node_is_product_operator(&nodes[idx]) {
217 nodes.remove(idx);
218 let mut i = idx;
219 for nested_node in children {
220 nodes.insert(i, nested_node);
221 i += 1;
222 }
223 } else {
224 idx += 1;
225 }
226 }
227
228 let mut number: Option<usize> = None;
232 let mut idx = 0;
233 while idx < nodes.len() {
234 match &nodes[idx] {
235 CalcNode::Number(_) => {
236 if let Some(prev_idx) = number {
237 let previous_value = get_value(&nodes[prev_idx]);
238 let value = get_value(&nodes[idx]);
239 if let Some(result) = try_to_multiply_values(previous_value, value) {
240 set_value(&mut nodes[prev_idx], result);
241 nodes.remove(idx);
242 } else {
243 idx += 1;
244 }
245 } else {
246 number = Some(idx);
247 idx += 1;
248 }
249 }
250 _ => {
251 idx += 1;
252 }
253 }
254 }
255
256 if nodes.len() == 2 {
261 match (&nodes[0], &nodes[1]) {
262 (CalcNode::Number(Number { value, .. }), CalcNode::OperatorNode(op))
263 | (CalcNode::OperatorNode(op), CalcNode::Number(Number { value, .. })) => {
264 if let CalcOperatorNode::Sum(children) = &**op {
265 if let Some(sum) =
266 try_to_multiply_all_numeric_sum_children_by_value(children, *value)
267 {
268 return sum;
269 }
270 }
271 }
272 _ => {}
273 }
274 }
275
276 nodes = try_to_multiply_all_numeric_and_invert_nodes(&nodes);
284
285 if nodes.len() == 1 {
286 nodes[0].clone()
287 } else {
288 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Product(nodes.clone())))
289 }
290}
291
292fn simplify_calc_operator_node_sum(nodes: &[CalcNode]) -> CalcNode {
294 let mut nodes = nodes.to_vec();
295
296 let mut idx = 0;
299 while idx < nodes.len() {
300 nodes[idx] = simplify_calc_node(&nodes[idx]);
301 if let Some(children) = get_children_if_node_is_sum_operator(&nodes[idx]) {
302 nodes.remove(idx);
303 let mut i = idx;
304 for nested_node in children {
305 nodes.insert(i, nested_node);
306 i += 1;
307 }
308 } else {
309 idx += 1;
310 }
311 }
312
313 let mut number: Option<usize> = None;
318 let mut percentage: Option<usize> = None;
319 let mut dimensions: FxHashMap<String, usize> = FxHashMap::default();
320 let mut idx = 0;
321 while idx < nodes.len() {
322 match &nodes[idx] {
323 CalcNode::Number(_) => {
324 if let Some(prev_idx) = number {
325 if try_to_sum_nodes(&mut nodes, prev_idx, idx) {
326 nodes.remove(idx);
327 } else {
328 idx += 1;
329 }
330 } else {
331 number = Some(idx);
332 idx += 1;
333 }
334 }
335 CalcNode::Dimension(d) => {
336 let unit = get_dimension_unit_lowercase(d);
337 match &dimensions.get(&*unit) {
338 Some(prev_idx) => {
339 if try_to_sum_nodes(&mut nodes, **prev_idx, idx) {
340 nodes.remove(idx);
341 } else {
342 idx += 1;
343 }
344 }
345 None => {
346 dimensions.insert(unit.to_string(), idx);
347 idx += 1;
348 }
349 }
350 }
351 CalcNode::Percentage(_) => {
352 if let Some(prev_idx) = percentage {
353 if try_to_sum_nodes(&mut nodes, prev_idx, idx) {
354 nodes.remove(idx);
355 } else {
356 idx += 1;
357 }
358 } else {
359 percentage = Some(idx);
360 idx += 1;
361 }
362 }
363 _ => {
364 idx += 1;
365 }
366 }
367 }
368
369 try_to_reduce_node_with_dimensions(&mut dimensions, &mut nodes);
371
372 if nodes.len() == 1 {
375 nodes[0].clone()
376 } else {
377 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Sum(nodes.clone())))
378 }
379}
380
381fn get_children_if_node_is_sum_operator(calc_node: &CalcNode) -> Option<Vec<CalcNode>> {
382 match calc_node {
383 CalcNode::OperatorNode(op) => match &**op {
384 CalcOperatorNode::Sum(children) => Some(children.clone()),
385 _ => None,
386 },
387 _ => None,
388 }
389}
390
391fn get_children_if_node_is_product_operator(calc_node: &CalcNode) -> Option<Vec<CalcNode>> {
392 match calc_node {
393 CalcNode::OperatorNode(op) => match &**op {
394 CalcOperatorNode::Product(children) => Some(children.clone()),
395 _ => None,
396 },
397 _ => None,
398 }
399}
400
401fn try_to_switch_sign_of_nodes(nodes: &[CalcNode]) -> Option<CalcNode> {
403 let mut nodes = nodes.to_vec();
404 let mut idx = 0;
405 while idx < nodes.len() {
406 let calc_node = &nodes[idx];
407 nodes[idx] = match calc_node {
408 CalcNode::Number(_) | CalcNode::Dimension(_) | CalcNode::Percentage(_) => {
411 let mut negated_node = calc_node.clone();
412 set_value(&mut negated_node, -get_value(calc_node));
413 negated_node
414 }
415 CalcNode::OperatorNode(op) => {
416 match &**op {
417 CalcOperatorNode::Negate(node) => node.clone(),
419 CalcOperatorNode::Sum(nodes) => {
420 try_to_switch_sign_of_nodes(nodes)?
423 }
424 _ => return None,
425 }
426 }
427 _ => {
428 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Negate(calc_node.clone())))
430 }
431 };
432 idx += 1;
433 }
434
435 if nodes.len() == 1 {
436 Some(nodes[0].clone())
437 } else {
438 Some(CalcNode::OperatorNode(Box::new(CalcOperatorNode::Sum(
439 nodes.to_vec(),
440 ))))
441 }
442}
443
444fn try_to_reduce_node_with_dimensions(
445 dimensions: &mut FxHashMap<String, usize>,
446 nodes: &mut Vec<CalcNode>,
447) {
448 try_to_reduce_node_with_absolute_lengths(dimensions, nodes);
449 try_to_reduce_node_with_durations(dimensions, nodes);
450 try_to_reduce_node_with_frequencies(dimensions, nodes);
451 try_to_reduce_node_with_resolutions(dimensions, nodes);
452}
453
454fn try_to_reduce_node_with_absolute_lengths(
456 dimensions: &mut FxHashMap<String, usize>,
457 nodes: &mut Vec<CalcNode>,
458) {
459 if let (Some(idx_cm), Some(idx_mm)) = (dimensions.get("cm"), dimensions.get("mm")) {
460 let value_cm = get_value(&nodes[*idx_cm]);
461 let value_mm = get_value(&nodes[*idx_mm]);
462 if let Some(result) = try_to_sum_values(value_cm, value_mm, Some(10.0)) {
463 set_value(&mut nodes[*idx_mm], result);
464 nodes.remove(*idx_cm);
465 }
466 }
467
468 if let (Some(idx_mm), Some(idx_q)) = (dimensions.get("mm"), dimensions.get("q")) {
469 let value_mm = get_value(&nodes[*idx_mm]);
470 let value_q = get_value(&nodes[*idx_q]);
471 if let Some(result) = try_to_sum_values(value_mm, value_q, Some(4.0)) {
472 set_value(&mut nodes[*idx_q], result);
473 nodes.remove(*idx_mm);
474 }
475 }
476
477 if let (Some(idx_cm), Some(idx_q)) = (dimensions.get("cm"), dimensions.get("q")) {
478 let value_cm = get_value(&nodes[*idx_cm]);
479 let value_q = get_value(&nodes[*idx_q]);
480 if let Some(result) = try_to_sum_values(value_cm, value_q, Some(40.0)) {
481 set_value(&mut nodes[*idx_q], result);
482 nodes.remove(*idx_cm);
483 }
484 }
485
486 if let (Some(idx_in), Some(idx_px)) = (dimensions.get("in"), dimensions.get("px")) {
487 let value_in = get_value(&nodes[*idx_in]);
488 let value_px = get_value(&nodes[*idx_px]);
489 if let Some(result) = try_to_sum_values(value_in, value_px, Some(96.0)) {
490 set_value(&mut nodes[*idx_px], result);
491 nodes.remove(*idx_in);
492 }
493 }
494
495 if let (Some(idx_in), Some(idx_pc)) = (dimensions.get("in"), dimensions.get("pc")) {
496 let value_in = get_value(&nodes[*idx_in]);
497 let value_pc = get_value(&nodes[*idx_pc]);
498 if let Some(result) = try_to_sum_values(value_in, value_pc, Some(6.0)) {
499 set_value(&mut nodes[*idx_pc], result);
500 nodes.remove(*idx_in);
501 }
502 }
503
504 if let (Some(idx_pc), Some(idx_pt)) = (dimensions.get("pc"), dimensions.get("pt")) {
505 let value_pc = get_value(&nodes[*idx_pc]);
506 let value_pt = get_value(&nodes[*idx_pt]);
507 if let Some(result) = try_to_sum_values(value_pc, value_pt, Some(12.0)) {
508 set_value(&mut nodes[*idx_pt], result);
509 nodes.remove(*idx_pc);
510 }
511 }
512
513 if let (Some(idx_pc), Some(idx_px)) = (dimensions.get("pc"), dimensions.get("px")) {
514 let value_pc = get_value(&nodes[*idx_pc]);
515 let value_px = get_value(&nodes[*idx_px]);
516 if let Some(result) = try_to_sum_values(value_pc, value_px, Some(16.0)) {
517 set_value(&mut nodes[*idx_px], result);
518 nodes.remove(*idx_pc);
519 }
520 }
521}
522
523fn try_to_reduce_node_with_durations(
525 dimensions: &mut FxHashMap<String, usize>,
526 nodes: &mut Vec<CalcNode>,
527) {
528 if let (Some(idx_ms), Some(idx_s)) = (dimensions.get("ms"), dimensions.get("s")) {
529 let value_ms = get_value(&nodes[*idx_ms]);
530 let value_s = get_value(&nodes[*idx_s]);
531 if let Some(result) = try_to_sum_values(value_s, value_ms, Some(1000.0)) {
532 set_value(&mut nodes[*idx_ms], result);
533 nodes.remove(*idx_s);
534 dimensions.remove("s");
535 }
536 }
537}
538
539fn try_to_reduce_node_with_frequencies(
541 dimensions: &mut FxHashMap<String, usize>,
542 nodes: &mut Vec<CalcNode>,
543) {
544 if let (Some(idx_hz), Some(idx_khz)) = (dimensions.get("hz"), dimensions.get("khz")) {
545 let value_hz = get_value(&nodes[*idx_hz]);
546 let value_khz = get_value(&nodes[*idx_khz]);
547 if let Some(result) = try_to_sum_values(value_khz, value_hz, Some(1000.0)) {
548 set_value(&mut nodes[*idx_hz], result);
549 nodes.remove(*idx_khz);
550 dimensions.remove("khz");
551 }
552 }
553}
554
555fn try_to_reduce_node_with_resolutions(
557 dimensions: &mut FxHashMap<String, usize>,
558 nodes: &mut Vec<CalcNode>,
559) {
560 match (dimensions.get("dppx"), dimensions.get("x")) {
561 (Some(idx_dppx), Some(idx_x)) => {
563 let value_dppx = get_value(&nodes[*idx_dppx]);
564 let value_x = get_value(&nodes[*idx_x]);
565 if let Some(result) = try_to_sum_values(value_x, value_dppx, None) {
566 set_value(&mut nodes[*idx_x], result);
567 nodes.remove(*idx_dppx);
568 dimensions.remove("dppx");
569 }
570 }
571 (Some(idx_dppx), None) => {
572 if let CalcNode::Dimension(Dimension::Resolution(r)) = &mut nodes[*idx_dppx] {
574 r.unit.value = atom!("x");
575 dimensions.insert("x".into(), *idx_dppx);
576 dimensions.remove("dppx");
577 }
578 }
579 _ => {}
580 }
581
582 if let (Some(idx_x), Some(idx_dpi)) = (dimensions.get("x"), dimensions.get("dpi")) {
583 let value_x = get_value(&nodes[*idx_x]);
584 let value_dpi = get_value(&nodes[*idx_dpi]);
585 if let Some(result) = try_to_sum_values(value_x, value_dpi, Some(96.0)) {
586 set_value(&mut nodes[*idx_dpi], result);
587 nodes.remove(*idx_x);
588 dimensions.remove("x");
589 }
590 }
591
592 if let (Some(idx_dpcm), Some(idx_dpi)) = (dimensions.get("dpcm"), dimensions.get("dpi")) {
593 let value_dpcm = get_value(&nodes[*idx_dpcm]);
594 let value_dpi = get_value(&nodes[*idx_dpi]);
595 if let Some(result) = try_to_sum_values(value_dpcm, value_dpi, Some(2.54)) {
596 set_value(&mut nodes[*idx_dpi], result);
597 nodes.remove(*idx_dpcm);
598 dimensions.remove("dpcm");
599 }
600 }
601}
602
603fn try_to_multiply_all_numeric_sum_children_by_value(
604 nodes: &[CalcNode],
605 value: f64,
606) -> Option<CalcNode> {
607 let mut operands = Vec::new();
608
609 for calc_node in nodes {
610 match calc_node {
611 CalcNode::Number(_) | CalcNode::Dimension(_) | CalcNode::Percentage(_) => {
612 let node_value = get_value(calc_node);
613 if let Some(result) = try_to_multiply_values(node_value, value) {
614 let mut node = calc_node.clone();
615 set_value(&mut node, result);
616 operands.push(node);
617 } else {
618 return None;
619 }
620 }
621 _ => return None,
622 }
623 }
624
625 Some(CalcNode::OperatorNode(Box::new(CalcOperatorNode::Sum(
626 operands,
627 ))))
628}
629
630fn try_to_invert_if_children_are_fraction(children: &[CalcNode]) -> Option<CalcNode> {
631 if children.len() == 2 {
632 match (&children[0], &children[1]) {
633 (CalcNode::OperatorNode(_), _) => None,
634 (numerator, CalcNode::OperatorNode(op)) => {
635 if let CalcOperatorNode::Invert(denominator) = &**op {
636 Some(CalcNode::OperatorNode(Box::new(CalcOperatorNode::Product(
637 vec![
638 denominator.clone(),
639 CalcNode::OperatorNode(Box::new(CalcOperatorNode::Invert(
640 numerator.clone(),
641 ))),
642 ],
643 ))))
644 } else {
645 None
646 }
647 }
648 (_, _) => None,
649 }
650 } else {
651 None
652 }
653}
654
655fn try_to_multiply_all_numeric_and_invert_nodes(children: &[CalcNode]) -> Vec<CalcNode> {
660 let mut nodes = children.to_vec();
661
662 let mut numeric: Option<usize> = None;
663 let mut idx = 0;
664 while idx < nodes.len() {
665 match numeric {
666 None => {
667 match &nodes[idx] {
668 CalcNode::Number(_) | CalcNode::Dimension(_) | CalcNode::Percentage(_) => {
669 numeric = Some(idx);
670 }
671 CalcNode::OperatorNode(op) => {
672 if let CalcOperatorNode::Invert(CalcNode::Number(_)) = &**op {
673 numeric = Some(idx);
674 }
675 }
676 _ => {}
677 };
678 idx += 1;
679 }
680 Some(prev_idx) => {
681 let prev_numeric_node = &nodes[prev_idx];
682 let cur_numeric_node = &nodes[idx];
683 match (prev_numeric_node, cur_numeric_node) {
684 (CalcNode::Number(_), other_node @ CalcNode::Number(_))
688 | (CalcNode::Number(_), other_node @ CalcNode::Percentage(_))
689 | (other_node @ CalcNode::Percentage(_), CalcNode::Number(_))
690 | (CalcNode::Number(_), other_node @ CalcNode::Dimension(_))
691 | (other_node @ CalcNode::Dimension(_), CalcNode::Number(_)) => {
692 let prev_value = get_value(prev_numeric_node);
693 let value = get_value(cur_numeric_node);
694 if let Some(result) = try_to_multiply_values(prev_value, value) {
695 nodes[prev_idx] = other_node.clone();
696 set_value(&mut nodes[prev_idx], result);
697 nodes.remove(idx);
698 } else {
699 idx += 1;
700 }
701 }
702 (CalcNode::OperatorNode(prev_op), CalcNode::OperatorNode(op)) => {
704 if let CalcOperatorNode::Invert(prev_numeric_node @ CalcNode::Number(_)) =
705 &**prev_op
706 {
707 if let CalcOperatorNode::Invert(
708 cur_numeric_node @ CalcNode::Number(_),
709 ) = &**op
710 {
711 let prev_value = get_value(prev_numeric_node);
712 let value = get_value(cur_numeric_node);
713 if let Some(result) = try_to_multiply_values(prev_value, value) {
714 let mut result_node = prev_numeric_node.clone();
715 set_value(&mut result_node, result);
716 nodes[prev_idx] = CalcNode::OperatorNode(Box::new(
717 CalcOperatorNode::Invert(result_node),
718 ));
719 nodes.remove(idx);
720 } else {
721 idx += 1;
722 }
723 } else {
724 idx += 1;
725 }
726 } else {
727 idx += 1;
728 }
729 }
730 (numeric_node @ CalcNode::Number(_), CalcNode::OperatorNode(op))
734 | (CalcNode::OperatorNode(op), numeric_node @ CalcNode::Number(_))
735 | (numeric_node @ CalcNode::Dimension(_), CalcNode::OperatorNode(op))
736 | (CalcNode::OperatorNode(op), numeric_node @ CalcNode::Dimension(_))
737 | (numeric_node @ CalcNode::Percentage(_), CalcNode::OperatorNode(op))
738 | (CalcNode::OperatorNode(op), numeric_node @ CalcNode::Percentage(_)) => {
739 if let CalcOperatorNode::Invert(inverted_node @ CalcNode::Number(_)) = &**op
740 {
741 let numerator = get_value(numeric_node);
742 let denominator = get_value(inverted_node);
743 if let Some(result) = try_to_divide_values(numerator, denominator) {
744 nodes[prev_idx] = numeric_node.clone();
745 set_value(&mut nodes[prev_idx], result);
746 nodes.remove(idx);
747 } else {
748 idx += 1;
749 }
750 } else {
751 idx += 1;
752 }
753 }
754 (CalcNode::Percentage(_), CalcNode::Percentage(_))
755 | (CalcNode::Percentage(_), CalcNode::Dimension(_))
756 | (CalcNode::Dimension(_), CalcNode::Percentage(_))
757 | (CalcNode::Dimension(_), CalcNode::Dimension(_)) => {
758 idx += 1;
760 }
761 _ => idx += 1,
763 }
764 }
765 }
766 }
767
768 nodes
769}
770
771fn try_to_multiply_values(v1: f64, v2: f64) -> Option<f64> {
772 let result = v1 * v2;
773
774 let precision1 = get_precision(v1);
775 let precision2 = get_precision(v2);
776 let result_precision = get_precision(result);
777
778 if result_precision <= (precision1 + precision2) {
779 Some(result)
780 } else {
781 None
782 }
783}
784
785fn try_to_sum_nodes(nodes: &mut [CalcNode], prev_idx: usize, idx: usize) -> bool {
786 let previous_value = get_value(&nodes[prev_idx]);
787 let value = get_value(&nodes[idx]);
788 if let Some(result) = try_to_sum_values(previous_value, value, None) {
789 set_value(&mut nodes[prev_idx], result);
790 true
791 } else {
792 false
793 }
794}
795
796fn try_to_sum_values(n1: f64, n2: f64, ratio: Option<f64>) -> Option<f64> {
798 let result = match ratio {
799 None => n1 + n2,
800 Some(r) => n1.mul_add(r, n2),
801 };
802
803 let precision1 = get_precision(n1);
804 let precision2 = get_precision(n2) + ratio.map_or(0, get_precision);
805 let result_precision = get_precision(result);
806
807 if result_precision <= precision1.max(precision2) {
808 Some(result)
809 } else {
810 None
811 }
812}
813
814fn try_to_divide_values(numerator: f64, denominator: f64) -> Option<f64> {
815 let result = numerator / denominator;
816 let result_precision = get_precision(result);
817
818 if result_precision <= f64::DIGITS {
819 Some(result)
820 } else {
821 None
822 }
823}
824
825fn serialize_calculation_node_into_calc_sum(calc_node: &CalcNode) -> CalcSum {
830 match calc_node {
831 CalcNode::Number(_)
832 | CalcNode::Dimension(_)
833 | CalcNode::Percentage(_)
834 | CalcNode::Constant(_)
835 | CalcNode::Function(_) => CalcSum {
836 expressions: vec![serialize_calc_node_into_calc_product(calc_node)],
837 span: Span::dummy_with_cmt(),
838 },
839 CalcNode::OperatorNode(op) => match &**op {
840 CalcOperatorNode::Sum(nodes) => {
841 let mut expr: Vec<CalcProductOrOperator> = Vec::new();
842
843 let nodes = sort_calculations_children(nodes);
844
845 let mut nodes_iter = nodes.iter();
846
847 if let Some(calc_node) = nodes_iter.next() {
848 expr.push(serialize_calc_node_into_calc_product(calc_node));
849 }
850
851 for calc_node in nodes_iter {
852 match calc_node {
853 CalcNode::Number(_) | CalcNode::Dimension(_) | CalcNode::Percentage(_) => {
854 let value = get_value(calc_node);
855 if value.is_sign_negative() {
856 let mut node = calc_node.clone();
858 set_value(&mut node, -value);
859 expr.push(CalcProductOrOperator::Operator(CalcOperator {
860 value: CalcOperatorType::Sub,
861 span: Span::dummy_with_cmt(),
862 }));
863 expr.push(serialize_calc_node_into_calc_product(&node));
864 } else {
865 expr.push(CalcProductOrOperator::Operator(CalcOperator {
866 value: CalcOperatorType::Add,
867 span: Span::dummy_with_cmt(),
868 }));
869 expr.push(serialize_calc_node_into_calc_product(calc_node));
870 }
871 }
872 CalcNode::Constant(_) | CalcNode::Function(_) => {
873 expr.push(CalcProductOrOperator::Operator(CalcOperator {
874 value: CalcOperatorType::Add,
875 span: Span::dummy_with_cmt(),
876 }));
877 expr.push(serialize_calc_node_into_calc_product(calc_node));
878 }
879 CalcNode::OperatorNode(op) => match &**op {
880 CalcOperatorNode::Product(_) => {
881 expr.push(CalcProductOrOperator::Operator(CalcOperator {
882 value: CalcOperatorType::Add,
883 span: Span::dummy_with_cmt(),
884 }));
885 expr.push(serialize_calc_node_into_calc_product(calc_node));
886 }
887 CalcOperatorNode::Negate(calc_node) => {
888 expr.push(CalcProductOrOperator::Operator(CalcOperator {
889 value: CalcOperatorType::Sub,
890 span: Span::dummy_with_cmt(),
891 }));
892 expr.push(serialize_calc_node_into_calc_product(calc_node));
893 }
894 _ => unreachable!("Cannot transform sum children into CalcProduct"),
895 },
896 }
897 }
898 CalcSum {
899 expressions: expr,
900 span: Span::dummy_with_cmt(),
901 }
902 }
903 _ => CalcSum {
904 expressions: vec![serialize_calc_node_into_calc_product(calc_node)],
905 span: Span::dummy_with_cmt(),
906 },
907 },
908 }
909}
910
911fn serialize_calc_node_into_calc_product(calc_node: &CalcNode) -> CalcProductOrOperator {
912 match calc_node {
913 CalcNode::Number(_)
914 | CalcNode::Dimension(_)
915 | CalcNode::Percentage(_)
916 | CalcNode::Constant(_)
917 | CalcNode::Function(_) => CalcProductOrOperator::Product(CalcProduct {
918 expressions: vec![CalcValueOrOperator::Value(
919 serialize_calc_node_into_calc_value(calc_node),
920 )],
921 span: Span::dummy_with_cmt(),
922 }),
923 CalcNode::OperatorNode(op) => match &**op {
924 CalcOperatorNode::Negate(_) => CalcProductOrOperator::Product(CalcProduct {
925 expressions: vec![
926 CalcValueOrOperator::Value(CalcValue::Number(Number {
927 value: -1.0,
928 span: Span::dummy_with_cmt(),
929 raw: None,
930 })),
931 CalcValueOrOperator::Operator(CalcOperator {
932 value: CalcOperatorType::Mul,
933 span: Span::dummy_with_cmt(),
934 }),
935 CalcValueOrOperator::Value(serialize_calc_node_into_calc_value(calc_node)),
936 ],
937 span: Span::dummy_with_cmt(),
938 }),
939 CalcOperatorNode::Invert(_) => CalcProductOrOperator::Product(CalcProduct {
940 expressions: vec![
941 CalcValueOrOperator::Value(CalcValue::Number(Number {
942 value: 1.0,
943 span: Span::dummy_with_cmt(),
944 raw: None,
945 })),
946 CalcValueOrOperator::Operator(CalcOperator {
947 value: CalcOperatorType::Div,
948 span: Span::dummy_with_cmt(),
949 }),
950 CalcValueOrOperator::Value(serialize_calc_node_into_calc_value(calc_node)),
951 ],
952 span: Span::dummy_with_cmt(),
953 }),
954 CalcOperatorNode::Product(nodes) => {
955 let mut expr: Vec<CalcValueOrOperator> = Vec::new();
956
957 let nodes = sort_calculations_children(nodes);
958
959 let mut nodes_iter = nodes.iter();
960
961 if let Some(calc_node) = nodes_iter.next() {
962 expr.push(CalcValueOrOperator::Value(
963 serialize_calc_node_into_calc_value(calc_node),
964 ))
965 }
966
967 for calc_node in nodes_iter {
968 match calc_node {
969 CalcNode::Number(_)
970 | CalcNode::Dimension(_)
971 | CalcNode::Percentage(_)
972 | CalcNode::Constant(_)
973 | CalcNode::Function(_) => {
974 expr.push(CalcValueOrOperator::Operator(CalcOperator {
975 value: CalcOperatorType::Mul,
976 span: Span::dummy_with_cmt(),
977 }));
978 expr.push(CalcValueOrOperator::Value(
979 serialize_calc_node_into_calc_value(calc_node),
980 ));
981 }
982 CalcNode::OperatorNode(op) => match &**op {
983 CalcOperatorNode::Invert(calc_node) => {
984 expr.push(CalcValueOrOperator::Operator(CalcOperator {
985 value: CalcOperatorType::Div,
986 span: Span::dummy_with_cmt(),
987 }));
988 expr.push(CalcValueOrOperator::Value(
989 serialize_calc_node_into_calc_value(calc_node),
990 ));
991 }
992 CalcOperatorNode::Product(_) | CalcOperatorNode::Sum(_) => {
993 expr.push(CalcValueOrOperator::Operator(CalcOperator {
994 value: CalcOperatorType::Mul,
995 span: Span::dummy_with_cmt(),
996 }));
997 expr.push(CalcValueOrOperator::Value(
998 serialize_calc_node_into_calc_value(calc_node),
999 ));
1000 }
1001 _ => unreachable!("Cannot transform product children into CalcProduct"),
1002 },
1003 }
1004 }
1005 CalcProductOrOperator::Product(CalcProduct {
1006 expressions: expr,
1007 span: Span::dummy_with_cmt(),
1008 })
1009 }
1010 CalcOperatorNode::Sum(_) => CalcProductOrOperator::Product(CalcProduct {
1011 expressions: vec![CalcValueOrOperator::Value(
1012 serialize_calc_node_into_calc_value(calc_node),
1013 )],
1014 span: Span::dummy_with_cmt(),
1015 }),
1016 },
1017 }
1018}
1019
1020fn serialize_calc_node_into_calc_value(calc_node: &CalcNode) -> CalcValue {
1021 match calc_node {
1022 CalcNode::Number(n) => CalcValue::Number(n.clone()),
1023 CalcNode::Dimension(d) => CalcValue::Dimension(d.clone()),
1024 CalcNode::Percentage(p) => CalcValue::Percentage(p.clone()),
1025 CalcNode::Constant(i) => CalcValue::Constant(i.clone()),
1026 CalcNode::Function(f) => CalcValue::Function(f.clone()),
1027 CalcNode::OperatorNode(op) => match &**op {
1028 CalcOperatorNode::Sum(_) => {
1029 CalcValue::Sum(serialize_calculation_node_into_calc_sum(calc_node))
1030 }
1031 CalcOperatorNode::Product(_) => CalcValue::Sum(CalcSum {
1032 expressions: vec![serialize_calc_node_into_calc_product(calc_node)],
1033 span: Span::dummy_with_cmt(),
1034 }),
1035 _ => unreachable!("Cannot transform CalcNode::OperatorNode into CalcValue"),
1036 },
1037 }
1038}
1039
1040fn sort_calculations_children(nodes: &[CalcNode]) -> Vec<CalcNode> {
1045 let mut ret: Vec<CalcNode> = Vec::new();
1046
1047 let mut numbers: Vec<CalcNode> = nodes
1049 .iter()
1050 .filter(|n| match n {
1051 CalcNode::Number(_) => true,
1052 _ => false,
1053 })
1054 .cloned()
1055 .collect();
1056 ret.append(&mut numbers);
1057
1058 let mut percentages: Vec<CalcNode> = nodes
1060 .iter()
1061 .filter(|n| match n {
1062 CalcNode::Percentage(_) => true,
1063 _ => false,
1064 })
1065 .cloned()
1066 .collect();
1067 ret.append(&mut percentages);
1068
1069 let mut dimensions: Vec<CalcNode> = nodes
1072 .iter()
1073 .filter(|n| match n {
1074 CalcNode::Dimension(_) => true,
1075 _ => false,
1076 })
1077 .cloned()
1078 .collect();
1079 dimensions.sort_by(|a, b| match (a, b) {
1080 (CalcNode::Dimension(d1), CalcNode::Dimension(d2)) => {
1081 let u1 = get_dimension_unit_lowercase(d1);
1082 let u2 = get_dimension_unit_lowercase(d2);
1083 u1.cmp(&u2)
1084 }
1085 _ => unreachable!("The vector should only contain dimensions"),
1086 });
1087 ret.append(&mut dimensions);
1088
1089 let mut any_items: Vec<CalcNode> = nodes
1091 .iter()
1092 .filter(|n| match n {
1093 CalcNode::Number(_) | CalcNode::Percentage(_) | CalcNode::Dimension(_) => false,
1094 _ => true,
1095 })
1096 .cloned()
1097 .collect();
1098 ret.append(&mut any_items);
1099
1100 if let Some(idx) = ret.iter().position(is_positive_value) {
1104 let positive_value = ret.remove(idx);
1106 ret.insert(0, positive_value);
1107 }
1108
1109 ret
1110}
1111
1112fn get_dimension_unit_lowercase(d: &Dimension) -> Atom {
1113 match d {
1114 Dimension::Length(l) => l.unit.value.to_ascii_lowercase(),
1115 Dimension::Angle(a) => a.unit.value.to_ascii_lowercase(),
1116 Dimension::Time(t) => t.unit.value.to_ascii_lowercase(),
1117 Dimension::Frequency(f) => f.unit.value.to_ascii_lowercase(),
1118 Dimension::Resolution(r) => r.unit.value.to_ascii_lowercase(),
1119 Dimension::Flex(f) => f.unit.value.to_ascii_lowercase(),
1120 Dimension::UnknownDimension(u) => u.unit.value.to_ascii_lowercase(),
1121 }
1122}
1123
1124fn is_positive_value(calc_node: &CalcNode) -> bool {
1125 match calc_node {
1126 CalcNode::Number(_) | CalcNode::Percentage(_) | CalcNode::Dimension(_) => {
1127 get_value(calc_node).is_sign_positive()
1128 }
1129 _ => false,
1130 }
1131}
1132
1133fn get_value(calc_node: &CalcNode) -> f64 {
1134 match calc_node {
1135 CalcNode::Number(n) => n.value,
1136 CalcNode::Percentage(p) => p.value.value,
1137 CalcNode::Dimension(Dimension::Length(l)) => l.value.value,
1138 CalcNode::Dimension(Dimension::Angle(a)) => a.value.value,
1139 CalcNode::Dimension(Dimension::Time(t)) => t.value.value,
1140 CalcNode::Dimension(Dimension::Frequency(f)) => f.value.value,
1141 CalcNode::Dimension(Dimension::Resolution(r)) => r.value.value,
1142 CalcNode::Dimension(Dimension::Flex(f)) => f.value.value,
1143 CalcNode::Dimension(Dimension::UnknownDimension(u)) => u.value.value,
1144 _ => unreachable!("Can only get a value from a value node"),
1145 }
1146}
1147
1148fn set_value(calc_node: &mut CalcNode, value: f64) {
1149 match calc_node {
1150 CalcNode::Number(n) => n.value = value,
1151 CalcNode::Percentage(p) => p.value.value = value,
1152 CalcNode::Dimension(Dimension::Length(l)) => l.value.value = value,
1153 CalcNode::Dimension(Dimension::Angle(a)) => a.value.value = value,
1154 CalcNode::Dimension(Dimension::Time(t)) => t.value.value = value,
1155 CalcNode::Dimension(Dimension::Frequency(f)) => f.value.value = value,
1156 CalcNode::Dimension(Dimension::Resolution(r)) => r.value.value = value,
1157 CalcNode::Dimension(Dimension::Flex(f)) => f.value.value = value,
1158 CalcNode::Dimension(Dimension::UnknownDimension(u)) => u.value.value = value,
1159 _ => unreachable!("Can only set a value into a value node"),
1160 }
1161}
1162
1163impl Compressor {
1164 pub(super) fn compress_calc_sum(&mut self, calc_sum: &mut CalcSum) {
1166 let root_calc_node = collect_calc_sum_into_calc_node(calc_sum);
1167
1168 let simplified_calc_tree = simplify_calc_node(&root_calc_node);
1169
1170 let simplified_calc_sum = serialize_calculation_node_into_calc_sum(&simplified_calc_tree);
1171 calc_sum.expressions = simplified_calc_sum.expressions;
1172 }
1173
1174 pub(super) fn compress_calc_sum_in_component_value(
1175 &mut self,
1176 component_value: &mut ComponentValue,
1177 ) {
1178 match &component_value {
1179 ComponentValue::Function(function)
1182 if is_calc_function_name(&function.name) && function.value.len() == 1 =>
1183 {
1184 match &function.value[0] {
1185 ComponentValue::CalcSum(calc_sum) if calc_sum.expressions.len() == 1 => {
1186 match &calc_sum.expressions[0] {
1187 CalcProductOrOperator::Product(CalcProduct {
1188 expressions: calc_product_expressions,
1189 ..
1190 }) if calc_product_expressions.len() == 1 => {
1191 if let CalcValueOrOperator::Value(calc_value) =
1192 &calc_product_expressions[0]
1193 {
1194 if let Some(cv) =
1195 transform_calc_value_into_component_value(calc_value)
1196 {
1197 *component_value = cv;
1198 }
1199 }
1200 }
1201 _ => {}
1202 }
1203 }
1204 _ => {}
1205 }
1206 }
1207 _ => {}
1208 }
1209 }
1210}