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 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}