summaryrefslogtreecommitdiffstats
path: root/cli/src/search.rs
blob: 54eea67c8550bd74ee5fb9013837272f2041fe25 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use chrono::{naive::NaiveDate, Duration};

use crate::transaction::{Category, Transaction};

pub struct Search<'t> {
    filtered: Vec<usize>,
    transactions: Vec<&'t Transaction>,
}

pub enum DateFilter {
    Absolute {
        start: Option<NaiveDate>,
        end: Option<NaiveDate>,
    },
    Relative {
        start: Option<Duration>,
        end: Option<Duration>,
    }
}

pub enum Constraint {
    Category(Category),
    Date(DateFilter),
}

impl Constraint {
    fn satisfies(&self, t: &Transaction) -> bool {
        match self {
            Constraint::Category(category) => category == &t.category,
            Constraint::Date(DateFilter::Relative {
                start,
                end,
            }) => {
                if let (Some(start), Some(end)) = (start, end) {
                    assert!(start < end);
                }
                let now = chrono::offset::Local::today().naive_utc();
                let start_valid = start.map(|start| t.date > now + start).unwrap_or(true);
                let end_valid = end.map(|end| t.date < now + end).unwrap_or(true);
                start_valid && end_valid
            },
            Constraint::Date(DateFilter::Absolute {
                start,
                end,
            }) => {
                if let (Some(start), Some(end)) = (start, end) {
                    assert!(start < end);
                }
                let now = chrono::offset::Local::today().naive_utc();
                let start_valid = start.map(|start| t.date > start).unwrap_or(true);
                let end_valid = end.map(|end| t.date < end).unwrap_or(true);
                start_valid && end_valid
            },
        }
    }
}

impl<'t> Search<'t> {
    pub fn new(transactions: Vec<&'t Transaction>) -> Self {
        Self {
            filtered: std::iter::successors(Some(0_usize), |n| Some(n.checked_add(1).unwrap())).take(transactions.len()).collect(),
            transactions,
        }
    }

    pub fn get(&self) -> Vec<&'t Transaction> {
        self
            .filtered
            .iter()
            .map(|idx| self.transactions[*idx])
            .collect()
    }

    pub fn subtract(self, constraint: Constraint) -> Self {
        Self {
            filtered: self
                .filtered
                .iter()
                .copied()
                .filter(|idx| !constraint.satisfies(self.transactions[*idx]))
                .collect(),
            transactions: self.transactions,
        }
    }
}