thisweek_core/
models.rs

1use crate::calendar::Calendar;
2use crate::calendar::CalendarLanguagePair;
3use cuid2;
4use diesel::prelude::*;
5use serde::Deserialize;
6use serde::Serialize;
7
8pub const ITEM_KIND_GOAL: i32 = 1;
9pub const ITEM_KIND_NOTE: i32 = 2;
10pub const ITEM_KIND_EVENT: i32 = 3;
11pub const STATUS_UNDONE: i32 = 0;
12pub const STATUS_DONE: i32 = 1;
13
14pub const LIST_TYPE_WEEKS: i32 = 1;
15pub const LIST_TYPE_OBJECTIVES: i32 = 2;
16
17#[derive(
18    Queryable, Selectable, Identifiable, AsChangeset, Debug, Serialize, Deserialize, Clone,
19)]
20#[diesel(table_name = crate::schema::items)]
21#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
22#[diesel(treat_none_as_null = true)]
23pub struct Item {
24    pub id: i32,
25    pub calendar: i32,
26    pub year: Option<i32>,
27    pub season: Option<i32>,
28    pub month: Option<i32>,
29    pub day: i32,
30    pub kind: i32,
31    pub fixed_date: bool,
32    pub all_day: bool,
33    pub title: Option<String>,
34    pub note: Option<String>,
35    pub datetime: Option<String>,
36    pub duration: Option<i32>,
37    pub status: Option<i32>,
38    pub order_in_week: Option<String>,
39    pub order_in_resolution: Option<String>,
40    pub sync: Option<i32>,
41    pub uuid: Option<String>,
42}
43
44#[derive(Debug, Serialize, Clone, Default)]
45pub struct ItemView {
46    pub id: i32,
47    pub calendar: i32,
48    pub kind: i32,
49    pub text: String,
50    pub status: bool,
51    pub fixed_day_tag: Option<String>,
52    pub objective_tag: Option<ObjectiveTag>,
53    pub uuid: Option<String>,
54}
55
56pub const OBJECTIVE_TYPE_NONE: i32 = 0;
57pub const OBJECTIVE_TYPE_MONTHLY: i32 = 1;
58pub const OBJECTIVE_TYPE_SEASONAL: i32 = 2;
59pub const OBJECTIVE_TYPE_YEARLY: i32 = 3;
60
61#[derive(Debug, Serialize, Clone, Default)]
62pub struct ObjectiveTag {
63    pub calendar: i32,
64    pub text: String,
65    pub r#type: i32,
66    pub calendar_name: String,
67    pub language: String,
68    pub year_string: String,
69    pub year: i32,
70    pub season: Option<usize>,
71    pub month: Option<usize>,
72}
73
74impl From<&Item> for ItemView {
75    fn from(item: &Item) -> Self {
76        let text: String = match item.kind {
77            ITEM_KIND_GOAL => item.title.clone().unwrap_or_default(),
78            ITEM_KIND_NOTE => item.note.clone().unwrap_or_default(),
79            _ => "".into(),
80        };
81        let status = { !matches!(item.status.unwrap_or_default(), 0) };
82        let fixed_day_tag = {
83            if item.day != 0 && item.fixed_date {
84                Some("todo: fixed date!".to_string())
85            } else {
86                None
87            }
88        };
89        let objective_tag = {
90            let cal: &Calendar = &item.calendar.into();
91            let cal_lang_pair: CalendarLanguagePair = cal.into();
92            cal_lang_pair.get_objective_tag(item.year, item.season, item.month)
93        };
94
95        ItemView {
96            id: item.id,
97            calendar: item.calendar,
98            kind: item.kind,
99            text,
100            status,
101            fixed_day_tag,
102            objective_tag,
103            uuid: item.uuid.clone(),
104        }
105    }
106}
107
108pub trait AsItemsList {
109    fn get_near_items_id(&self, id: i32) -> (Option<i32>, Option<i32>);
110}
111
112impl AsItemsList for Vec<Item> {
113    fn get_near_items_id(&self, id: i32) -> (Option<i32>, Option<i32>) {
114        let mut previous = None;
115        let mut next = None;
116        let mut iter = self.iter();
117        if id < 0 {
118            // this case is when nothing is selected.
119            // return first and last item's id
120            if let Some(item) = iter.next() {
121                next = Some(item.id);
122            }
123            if let Some(item) = iter.last() {
124                previous = Some(item.id);
125            }
126            (previous, next)
127        } else {
128            let position = iter.position(|i| (i.id == id));
129            if let Some(pos) = position {
130                if pos > 0 {
131                    previous = Some(self[pos - 1].id);
132                }
133                if pos < (self.len() - 1) {
134                    next = Some(self[pos + 1].id);
135                }
136                (previous, next)
137            } else {
138                (None, None)
139            }
140        }
141    }
142}
143
144#[derive(Insertable)]
145#[diesel(table_name = crate::schema::items)]
146#[diesel(treat_none_as_null = true)]
147#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
148pub struct NewItem {
149    pub calendar: i32,
150    pub year: Option<i32>,
151    pub season: Option<i32>,
152    pub month: Option<i32>,
153    pub day: i32,
154    pub kind: i32,
155    pub fixed_date: bool,
156    pub all_day: bool,
157    pub title: Option<String>,
158    pub note: Option<String>,
159    pub datetime: Option<String>,
160    pub duration: Option<i32>,
161    pub status: Option<i32>,
162    pub order_in_week: Option<String>,
163    pub order_in_resolution: Option<String>,
164    pub sync: Option<i32>,
165    pub uuid: Option<String>,
166}
167
168impl NewItem {
169    #[allow(clippy::too_many_arguments)]
170    pub fn new(
171        calendar: i32,
172        year: Option<i32>,
173        season: Option<i32>,
174        month: Option<i32>,
175        day: i32,
176        kind: i32,
177        text: String,
178        ordering_key: String,
179    ) -> Self {
180        let is_objective: bool = year.is_some();
181        NewItem {
182            calendar,
183            year,
184            season,
185            month,
186            day,
187            kind,
188            fixed_date: false,
189            all_day: false,
190            title: if kind == ITEM_KIND_GOAL {
191                Some(text.clone())
192            } else {
193                None
194            },
195            note: if kind == ITEM_KIND_NOTE {
196                Some(text.clone())
197            } else {
198                None
199            },
200            datetime: None,
201            duration: None,
202            status: Some(STATUS_UNDONE),
203            order_in_week: if is_objective {
204                None
205            } else {
206                Some(ordering_key.clone())
207            },
208            order_in_resolution: if is_objective {
209                Some(ordering_key.clone())
210            } else {
211                None
212            },
213            sync: None,
214            uuid: Some(cuid2::create_id()),
215        }
216    }
217
218    pub fn from(item: &Item) -> NewItem {
219        NewItem {
220            calendar: item.calendar,
221            year: item.year,
222            season: item.season,
223            month: item.month,
224            day: item.day,
225            kind: item.kind,
226            fixed_date: item.fixed_date,
227            all_day: item.all_day,
228            title: item.title.clone(),
229            note: item.note.clone(),
230            datetime: item.datetime.clone(),
231            duration: item.duration,
232            status: item.status,
233            order_in_week: item.order_in_week.clone(),
234            order_in_resolution: item.order_in_resolution.clone(),
235            sync: None,
236            uuid: Some(cuid2::create_id()),
237        }
238    }
239}