1use chrono::{NaiveDate, Duration};
2
3use crate::parser::{
4 parse_str,
5 ast::Expr
6};
7use crate::reference::Reference;
8use crate::errors::Error;
9
10pub fn excel_to_date(serial: f64) -> NaiveDate {
11 let start_date = NaiveDate::from_ymd(1899, 12, 30);
12 let mut duration = Duration::days(serial as i64);
13 let frac = (serial - serial.floor()) as i64;
14 if frac > 0 {
15 duration = duration.checked_add(&Duration::hours(24 * frac)).expect("Could not add two dates together");
16 }
17 start_date.checked_add_signed(duration).unwrap()
18}
19
20pub fn adjust_formula(
21 base_reference: Reference,
22 current_reference: Reference,
23 formula_text: String,
24) -> Result<String, Error> {
25 let row_offset: i32 = current_reference.row() as i32 - base_reference.row() as i32;
26 let column_offset: i32 = current_reference.column() as i32 - base_reference.column() as i32;
27 let mut expression: Expr = parse_str(&formula_text)?;
28 adjust_expression(row_offset, column_offset, &mut expression)?;
29 Ok(format!("{}", expression))
30}
31
32pub fn adjust_expression(
33 row_offset: i32,
34 column_offset: i32,
35 expression: &mut Expr
36) -> Result<(), Error> {
37 match *expression {
38 Expr::Reference { sheet: _, ref mut reference } => {
39 let mut r = Reference::from(reference.to_string());
40 r.offset((row_offset, column_offset));
41 *reference = r.to_string();
42 },
43 Expr::Infix(_, ref mut a, ref mut b) => {
44 adjust_expression(row_offset, column_offset, a)?;
45 adjust_expression(row_offset, column_offset, b)?;
46 },
47 Expr::Prefix(_, ref mut a) => {
48 adjust_expression(row_offset, column_offset, a)?;
49 },
50 Expr::Func { name: _, ref mut args } => {
51 for arg in args.iter_mut() {
52 adjust_expression(row_offset, column_offset, arg)?;
53 }
54 },
55 Expr::Array(ref mut arr) => {
56 for a in arr.iter_mut() {
57 adjust_expression(row_offset, column_offset, a)?;
58 }
59 }
60 _ => {}
61 }
62 Ok(())
63}
64
65#[cfg(test)]
66mod tests {
67 use crate::reference::Reference;
68 use crate::utils::adjust_formula;
69 use crate::errors::Error;
70
71 #[test]
72 fn test_adjust_formula() -> Result<(), Error> {
73 let base_reference = Reference::from((1, 1));
74 let current_reference = Reference::from((2, 1));
75 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("Sheet1!A1"))?, &"Sheet1!A2");
76 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("A1+A2"))?, &"(A2+A3)");
77 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("SUM(A1+A2)"))?, &"SUM((A2+A3))");
78 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("{A1, A2}"))?, &"{A2, A3}");
79 Ok(())
80 }
81
82 #[test]
83 fn test_adjust_formula_anchored() -> Result<(), Error> {
84 let base_reference = Reference::from((1, 1));
85 let current_reference = Reference::from((2, 2));
86 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("Sheet1!$A$1"))?, &"Sheet1!$A$1");
87 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("$A$1+A2"))?, &"($A$1+B3)");
88 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("SUM(A$1+A$2)"))?, &"SUM((B$1+B$2))");
89 assert_eq!(&adjust_formula(base_reference, current_reference, String::from("{A1, A2}"))?, &"{B2, B3}");
90 Ok(())
91 }
92}