aboutsummaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorJoris2022-03-19 22:03:58 +0100
committerJoris2022-03-19 22:03:58 +0100
commit35cc74578e969bae4812afd2ff041eba3746142d (patch)
treec0ef18d3c81554f0e70c91fcb1006d587470365c /src/model
parent199624dc03ead28ddc7454147457512d9568c593 (diff)
downloadcalendar-35cc74578e969bae4812afd2ff041eba3746142d.tar.gz
calendar-35cc74578e969bae4812afd2ff041eba3746142d.tar.bz2
calendar-35cc74578e969bae4812afd2ff041eba3746142d.zip
Allow to repeat an event until a specific date
Also provide a shortcut to modify a repetead event from a specific occurence. This set the end repetition date under the hood and create a new repeated event.
Diffstat (limited to 'src/model')
-rw-r--r--src/model/event.rs52
-rw-r--r--src/model/mod.rs1
-rw-r--r--src/model/repetition.rs21
-rw-r--r--src/model/time.rs22
4 files changed, 51 insertions, 45 deletions
diff --git a/src/model/event.rs b/src/model/event.rs
index 5e92692..e556f6e 100644
--- a/src/model/event.rs
+++ b/src/model/event.rs
@@ -1,9 +1,10 @@
-use chrono::Timelike;
use chrono::{NaiveDate, NaiveTime};
use std::collections::HashMap;
use uuid::Uuid;
use crate::model::repetition::Repetition;
+use crate::model::time;
+use crate::validation;
pub static DATE_FORMAT: &str = "%d/%m/%Y";
@@ -19,10 +20,10 @@ pub struct Event {
impl Event {
pub fn pprint(&self) -> String {
- let start = self.start.map(pprint_time).unwrap_or_default();
+ let start = self.start.map(time::pprint).unwrap_or_default();
let end = self
.end
- .map(|t| format!("-{}", pprint_time(t)))
+ .map(|t| format!("-{}", time::pprint(t)))
.unwrap_or_default();
let space = if self.start.is_some() || self.end.is_some() {
" "
@@ -52,27 +53,6 @@ pub fn repetitions_between(
res
}
-pub fn pprint_time(t: NaiveTime) -> String {
- if t.minute() == 0 {
- format!("{}h", t.hour())
- } else {
- format!("{}h{}", t.hour(), t.minute())
- }
-}
-
-fn parse_time(t: &str) -> Option<NaiveTime> {
- match t.split('h').collect::<Vec<&str>>()[..] {
- [hours, minutes] => {
- if minutes.trim().is_empty() {
- NaiveTime::from_hms_opt(hours.parse().ok()?, 0, 0)
- } else {
- NaiveTime::from_hms_opt(hours.parse().ok()?, minutes.parse().ok()?, 0)
- }
- }
- _ => None,
- }
-}
-
// Validation
pub fn validate(
@@ -83,8 +63,8 @@ pub fn validate(
end: String,
repetition: Option<Repetition>,
) -> Option<Event> {
- let start = validate_time(start)?;
- let end = validate_time(end)?;
+ let start = validation::time(start)?;
+ let end = validation::time(end)?;
match (start, end) {
(Some(s), Some(e)) if s > e => None?,
@@ -94,27 +74,9 @@ pub fn validate(
Some(Event {
id,
date: NaiveDate::parse_from_str(&date, DATE_FORMAT).ok()?,
- name: validate_name(name)?,
+ name: validation::non_empty(name)?,
start,
end,
repetition,
})
}
-
-fn validate_time(time: String) -> Option<Option<NaiveTime>> {
- let time = time.trim();
- if time.is_empty() {
- Some(None)
- } else {
- parse_time(time).map(Some)
- }
-}
-
-fn validate_name(name: String) -> Option<String> {
- let name = name.trim();
- if name.is_empty() {
- None
- } else {
- Some(name.to_string())
- }
-}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index c1beb62..0aefbc6 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -1,2 +1,3 @@
pub mod event;
pub mod repetition;
+pub mod time;
diff --git a/src/model/repetition.rs b/src/model/repetition.rs
index 872944a..eb8cb6d 100644
--- a/src/model/repetition.rs
+++ b/src/model/repetition.rs
@@ -6,6 +6,7 @@ use std::collections::HashSet;
pub struct Repetition {
pub frequency: Frequency,
pub removed_occurences: HashSet<usize>,
+ pub until: Option<NaiveDate>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -48,6 +49,7 @@ impl Repetition {
let repeat = |mut date, next: Box<dyn Fn(NaiveDate) -> NaiveDate>| {
let mut repetitions = vec![];
let mut iteration: usize = 0;
+ let end = self.until.unwrap_or(end);
while date <= end {
if date >= event {
if date >= start && !self.removed_occurences.contains(&iteration) {
@@ -195,6 +197,7 @@ mod tests {
let repetition = Repetition {
frequency: Frequency::Daily { period: 2 },
removed_occurences: HashSet::from([0, 2, 3]),
+ until: None,
};
assert_eq!(
repetition.between(d(2020, 7, 1), d(2020, 7, 1), d(2020, 7, 9)),
@@ -209,6 +212,7 @@ mod tests {
day: DayOfMonth::Day { day: 8 },
},
removed_occurences: HashSet::from([1, 3]),
+ until: None,
};
assert_eq!(
repetition.between(d(2020, 1, 8), d(2020, 1, 8), d(2020, 4, 8)),
@@ -225,6 +229,7 @@ mod tests {
},
},
removed_occurences: HashSet::from([1, 2, 3]),
+ until: None,
};
assert_eq!(
repetition.between(d(2020, 2, 1), d(2020, 2, 1), d(2020, 7, 1)),
@@ -237,6 +242,7 @@ mod tests {
let repetition = Repetition {
frequency: Frequency::Yearly,
removed_occurences: HashSet::from([3]),
+ until: None,
};
assert_eq!(
repetition.between(d(2018, 5, 5), d(2019, 8, 1), d(2022, 5, 5)),
@@ -249,6 +255,7 @@ mod tests {
let repetition = Repetition {
frequency: Frequency::Yearly,
removed_occurences: HashSet::from([1]),
+ until: None,
};
assert_eq!(
repetition.occurence_index(d(2020, 1, 1), d(2022, 1, 1)),
@@ -256,6 +263,19 @@ mod tests {
)
}
+ #[test]
+ fn repetition_stops_after_until() {
+ let repetition = Repetition {
+ frequency: Frequency::Yearly,
+ removed_occurences: HashSet::new(),
+ until: Some(d(2022, 1, 1)),
+ };
+ assert_eq!(
+ repetition.between(d(2020, 1, 1), d(2020, 1, 1), d(2024, 1, 1)),
+ vec!(d(2020, 1, 1), d(2021, 1, 1), d(2022, 1, 1))
+ )
+ }
+
fn d(y: i32, m: u32, d: u32) -> NaiveDate {
NaiveDate::from_ymd(y, m, d)
}
@@ -264,6 +284,7 @@ mod tests {
Repetition {
frequency,
removed_occurences: HashSet::new(),
+ until: None,
}
}
}
diff --git a/src/model/time.rs b/src/model/time.rs
new file mode 100644
index 0000000..10cf6d3
--- /dev/null
+++ b/src/model/time.rs
@@ -0,0 +1,22 @@
+use chrono::{NaiveTime, Timelike};
+
+pub fn pprint(t: NaiveTime) -> String {
+ if t.minute() == 0 {
+ format!("{}h", t.hour())
+ } else {
+ format!("{}h{}", t.hour(), t.minute())
+ }
+}
+
+pub fn parse(t: &str) -> Option<NaiveTime> {
+ match t.split('h').collect::<Vec<&str>>()[..] {
+ [hours, minutes] => {
+ if minutes.trim().is_empty() {
+ NaiveTime::from_hms_opt(hours.parse().ok()?, 0, 0)
+ } else {
+ NaiveTime::from_hms_opt(hours.parse().ok()?, minutes.parse().ok()?, 0)
+ }
+ }
+ _ => None,
+ }
+}