aboutsummaryrefslogtreecommitdiff
path: root/src/cli/mod.rs
blob: 8069a10b9b6bf0193dd28a5466fa9a892884e021 (plain)
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
86
87
88
89
90
use anyhow::Result;
use chrono::{Local, NaiveDate, NaiveDateTime, TimeDelta, TimeZone};
use rusqlite::Connection;
use std::ops::{Add, Sub};

use crate::model::event::Event;
use crate::model::category::Category;
use crate::{db, model::event};

pub fn parse_date(s: String) -> Option<NaiveDate> {
    match s.as_str() {
        "yesterday" => Some(Local::now().sub(TimeDelta::days(1)).date_naive()),
        "today" => Some(Local::now().date_naive()),
        "tomorrow" => Some(Local::now().add(TimeDelta::days(1)).date_naive()),
        _ => NaiveDate::parse_from_str(&s, "%Y-%m-%d").ok(),
    }
}

pub fn started_at_date(conn: &Connection, date: NaiveDate) -> Result<String> {
    let events = between_inclusive(conn, date, date)?;
    let categories = db::categories::list(conn)?;
    Ok(format_events(events, categories))
}

pub fn parse_timestamp_range(s: String) -> Option<(NaiveDateTime, NaiveDateTime)> {
    match s.split("..").collect::<Vec<&str>>()[..] {
        [from, to] => {
            let from = from.parse().ok()?;
            let to = to.parse().ok()?;

            let from = Local.timestamp_opt(from, 0).single()?;
            let to = Local.timestamp_opt(to, 0).single()?;

            Some((from.naive_local(), to.naive_local()))
        }
        _ => None,
    }
}

pub fn start_between(conn: &Connection, from: NaiveDateTime, to: NaiveDateTime) -> Result<String> {
    let from_date = from.date();
    let to_date = to.date();
    let events = between_inclusive(conn, from_date, to_date)?;
    let events: Vec<Event> = events
        .iter()
        .filter(|e| match e.start {
            None => false,
            Some(t) => {
                let dt = NaiveDateTime::new(e.date, t);
                dt >= from && dt < to
            }
        })
        .cloned()
        .collect::<Vec<Event>>();
    let categories = db::categories::list(conn)?;
    Ok(format_events(events, categories))
}

fn between_inclusive(conn: &Connection, from: NaiveDate, to: NaiveDate) -> Result<Vec<Event>> {
    let mut events = db::events::list_non_recurring_between(conn, from, to)?;
    let recurring_events = db::events::list_recurring(conn)?;
    let repetitions = event::repetitions_between(&recurring_events, from, to);
    for (date, original_events) in repetitions.iter() {
        for original_event in original_events {
            let event = Event {
                date: *date,
                ..original_event.clone()
            };
            events.push(event);
        }
    }
    Ok(events)
}

fn format_events(events: Vec<Event>, categories: Vec<Category>) -> String {
    let mut events = events;
    events.sort_by_key(|e| e.local_timestamp());
    events
        .iter()
        .map(|e| {
            let category = e.category.and_then(|c| categories.clone().into_iter().find(|c2| c == c2.id));
            let category_str = match category {
                Some(c) => format!("[{}] ", c.name),
                None => "".to_string()
            };
            return format!("{}{}\n", category_str, e.pprint())
        })
        .collect::<Vec<String>>()
        .join("")
}