entracte_lib/scheduler/commands/
chores.rs1use crate::chores_store::sanitize_items;
2
3use super::super::chores::{persist_chores, rollover_if_new_day, ChoresState};
4use super::super::timers::local_today_string;
5use super::super::Scheduler;
6
7#[tauri::command]
12pub async fn get_chores(scheduler: tauri::State<'_, Scheduler>) -> Result<ChoresState, String> {
13 Ok(get_chores_impl(scheduler.inner()).await)
14}
15
16#[tauri::command]
21pub async fn set_chores(
22 scheduler: tauri::State<'_, Scheduler>,
23 items: Vec<String>,
24) -> Result<ChoresState, String> {
25 Ok(set_chores_impl(scheduler.inner(), items).await)
26}
27
28pub(crate) async fn get_chores_impl(scheduler: &Scheduler) -> ChoresState {
30 let today = local_today_string();
31 let mut c = scheduler.chores.lock().await;
32 if rollover_if_new_day(&mut c, &today) {
33 persist_chores(&scheduler.chores_path, &c);
34 }
35 c.clone()
36}
37
38pub(crate) async fn set_chores_impl(scheduler: &Scheduler, items: Vec<String>) -> ChoresState {
40 let today = local_today_string();
41 let cleaned = sanitize_items(items);
42 let mut c = scheduler.chores.lock().await;
43 rollover_if_new_day(&mut c, &today);
44 c.items = cleaned;
45 if !c.items.is_empty() {
49 c.ever_used_chores = true;
50 }
51 persist_chores(&scheduler.chores_path, &c);
52 c.clone()
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::scheduler::types::BreakKind;
59 use crate::scheduler::Settings;
60 use crate::test_support::test_scheduler;
61
62 #[tokio::test]
63 async fn set_then_get_round_trips_and_sanitizes() {
64 let (_dir, sched) = test_scheduler(Settings::default());
65 let stored = set_chores_impl(
66 &sched,
67 vec![
68 " Water the plants ".to_string(),
69 " ".to_string(),
70 "Reply to Sam".to_string(),
71 ],
72 )
73 .await;
74 assert_eq!(
75 stored.items,
76 vec!["Water the plants".to_string(), "Reply to Sam".to_string()]
77 );
78 let read = get_chores_impl(&sched).await;
80 assert_eq!(read.items, stored.items);
81 }
82
83 #[tokio::test]
84 async fn set_chores_marks_and_keeps_the_chore_user_flag() {
85 let (_dir, sched) = test_scheduler(Settings::default());
86 let empty = set_chores_impl(&sched, vec![" ".to_string()]).await;
88 assert!(empty.items.is_empty());
89 assert!(!empty.ever_used_chores);
90 let used = set_chores_impl(&sched, vec!["Water the plants".to_string()]).await;
92 assert!(used.ever_used_chores);
93 let cleared = set_chores_impl(&sched, vec![]).await;
96 assert!(cleared.items.is_empty());
97 assert!(cleared.ever_used_chores);
98 }
99
100 #[tokio::test]
101 async fn get_rolls_over_a_stale_day_to_empty() {
102 let (_dir, sched) = test_scheduler(Settings::default());
103 set_chores_impl(&sched, vec!["Yesterday".to_string()]).await;
104 {
106 let mut c = sched.chores.lock().await;
107 c.date = "2000-01-01".to_string();
108 }
109 let read = get_chores_impl(&sched).await;
110 assert!(read.items.is_empty(), "stale list cleared on read");
111 }
112
113 #[tokio::test]
114 async fn resolve_chore_prompt_long_cycles_micro_skips() {
115 let (_dir, sched) = test_scheduler(Settings::default());
116 assert_eq!(sched.resolve_chore_prompt(BreakKind::Long).await, None);
118
119 set_chores_impl(&sched, vec!["a".to_string(), "b".to_string()]).await;
120 assert_eq!(
121 sched.resolve_chore_prompt(BreakKind::Long).await.as_deref(),
122 Some("a")
123 );
124 assert_eq!(
125 sched.resolve_chore_prompt(BreakKind::Long).await.as_deref(),
126 Some("b")
127 );
128 assert_eq!(sched.resolve_chore_prompt(BreakKind::Micro).await, None);
130 assert_eq!(
131 sched.resolve_chore_prompt(BreakKind::Long).await.as_deref(),
132 Some("a")
133 );
134 }
135}
136
137#[cfg(all(test, not(target_os = "windows")))]
142mod rig_tests {
143 use super::*;
144 use crate::scheduler::Settings;
145 use crate::test_support::mock_app_with_scheduler;
146 use tauri::Manager;
147
148 #[tokio::test]
149 async fn get_and_set_chores_commands_via_rig() {
150 let (_dir, app, _sched) = mock_app_with_scheduler(Settings::default());
151 let stored = set_chores(app.state::<Scheduler>(), vec!["Mow the lawn".to_string()])
152 .await
153 .expect("set_chores succeeds");
154 assert_eq!(stored.items, vec!["Mow the lawn".to_string()]);
155 let read = get_chores(app.state::<Scheduler>())
156 .await
157 .expect("get_chores succeeds");
158 assert_eq!(read.items, vec!["Mow the lawn".to_string()]);
159 }
160}