Skip to content

Commit d7aa647

Browse files
author
kiran-garre
committed
refactor: Convert from global database store to per-directory filesystem store
Updates: - Todo lists are now local to the user's cwd and are stored in files - Changed names from 'task_description'/'tasks' to 'todo_list_description'/'tasks'
1 parent e7ea8af commit d7aa647

File tree

6 files changed

+133
-107
lines changed

6 files changed

+133
-107
lines changed

crates/chat-cli/src/cli/chat/cli/todos.rs

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ pub enum TodoSubcommand {
2727
View,
2828

2929
/// Delete a to-do list
30-
Delete,
30+
Delete {
31+
#[arg(long, short)]
32+
all: bool,
33+
},
3134
}
3235

3336
/// Used for displaying completed and in-progress todo lists
@@ -59,24 +62,15 @@ impl TodoSubcommand {
5962
pub async fn execute(self, os: &mut Os, session: &mut ChatSession) -> Result<ChatState, ChatError> {
6063
match self {
6164
Self::ClearFinished => {
62-
let entries = match os.database.get_all_todos() {
63-
Ok(e) => e,
64-
Err(e) => return Err(ChatError::Custom(format!("Could not get all to-do lists: {e}").into())),
65+
let (todos, errors) = match TodoState::get_all_todos(os).await {
66+
Ok(res) => res,
67+
Err(e) => return Err(ChatError::Custom(format!("Could not get to-do lists: {e}").into())),
6568
};
6669
let mut cleared_one = false;
6770

68-
for (id, value) in entries.iter() {
69-
let todo_status = match value.as_str() {
70-
Some(s) => match serde_json::from_str::<TodoState>(s) {
71-
Ok(state) => state,
72-
73-
// FIX: Silent fail
74-
Err(_) => continue,
75-
},
76-
None => continue,
77-
};
71+
for todo_status in todos.iter() {
7872
if todo_status.completed.iter().all(|b| *b) {
79-
match os.database.delete_todo(id) {
73+
match TodoState::delete_todo(os, &todo_status.id).await {
8074
Ok(_) => cleared_one = true,
8175
Err(e) => {
8276
return Err(ChatError::Custom(format!("Could not delete to-do list: {e}").into()));
@@ -92,8 +86,14 @@ impl TodoSubcommand {
9286
} else {
9387
execute!(session.stderr, style::Print("No finished to-do lists to clear!\n"))?;
9488
}
89+
if !errors.is_empty() {
90+
execute!(
91+
session.stderr,
92+
style::Print(format!("* Failed to get {} todo list(s)\n", errors.len()).dark_grey())
93+
)?;
94+
}
9595
},
96-
Self::Resume => match Self::get_descriptions_and_statuses(os) {
96+
Self::Resume => match Self::get_descriptions_and_statuses(os).await {
9797
Ok(entries) => {
9898
if entries.is_empty() {
9999
execute!(session.stderr, style::Print("No to-do lists to resume!\n"),)?;
@@ -113,13 +113,13 @@ impl TodoSubcommand {
113113
},
114114
Err(e) => return Err(ChatError::Custom(format!("Could not show to-do lists: {e}").into())),
115115
},
116-
Self::View => match Self::get_descriptions_and_statuses(os) {
116+
Self::View => match Self::get_descriptions_and_statuses(os).await {
117117
Ok(entries) => {
118118
if entries.is_empty() {
119119
execute!(session.stderr, style::Print("No to-do lists to view!\n"))?;
120120
} else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to view:") {
121121
if index < entries.len() {
122-
let list = TodoState::load(os, &entries[index].id).map_err(|e| {
122+
let list = TodoState::load(os, &entries[index].id).await.map_err(|e| {
123123
ChatError::Custom(format!("Could not load current to-do list: {e}").into())
124124
})?;
125125
execute!(
@@ -137,15 +137,22 @@ impl TodoSubcommand {
137137
}
138138
}
139139
},
140-
Err(_) => return Err(ChatError::Custom("Could not show to-do lists".into())),
140+
Err(e) => return Err(ChatError::Custom(format!("Could not show to-do lists: {e}").into())),
141141
},
142-
Self::Delete => match Self::get_descriptions_and_statuses(os) {
142+
Self::Delete { all } => match Self::get_descriptions_and_statuses(os).await {
143143
Ok(entries) => {
144144
if entries.is_empty() {
145145
execute!(session.stderr, style::Print("No to-do lists to delete!\n"))?;
146+
} else if all {
147+
for entry in entries {
148+
TodoState::delete_todo(os, &entry.id)
149+
.await
150+
.map_err(|_e| ChatError::Custom("Could not delete all to-do lists".into()))?;
151+
}
152+
execute!(session.stderr, style::Print("✔ Deleted all to-do lists!\n".green()),)?;
146153
} else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to delete:") {
147154
if index < entries.len() {
148-
os.database.delete_todo(&entries[index].id).map_err(|e| {
155+
TodoState::delete_todo(os, &entries[index].id).await.map_err(|e| {
149156
ChatError::Custom(format!("Could not delete the selected to-do list: {e}").into())
150157
})?;
151158
execute!(
@@ -156,7 +163,7 @@ impl TodoSubcommand {
156163
}
157164
}
158165
},
159-
Err(_) => return Err(ChatError::Custom("Could not show to-do lists".into())),
166+
Err(e) => return Err(ChatError::Custom(format!("Could not show to-do lists: {e}").into())),
160167
},
161168
}
162169
Ok(ChatState::PromptUser {
@@ -165,23 +172,15 @@ impl TodoSubcommand {
165172
}
166173

167174
/// Convert all to-do list state entries to displayable entries
168-
fn get_descriptions_and_statuses(os: &Os) -> Result<Vec<TodoDisplayEntry>> {
175+
async fn get_descriptions_and_statuses(os: &Os) -> Result<Vec<TodoDisplayEntry>> {
169176
let mut out = Vec::new();
170-
let entries = os.database.get_all_todos()?;
171-
for (id, value) in entries.iter() {
172-
let temp_struct = match value.as_str() {
173-
Some(s) => match serde_json::from_str::<TodoState>(s) {
174-
Ok(state) => state,
175-
Err(_) => continue,
176-
},
177-
None => continue,
178-
};
179-
177+
let (todos, _) = TodoState::get_all_todos(os).await?;
178+
for todo in todos.iter() {
180179
out.push(TodoDisplayEntry {
181-
num_completed: temp_struct.completed.iter().filter(|b| **b).count(),
182-
num_tasks: temp_struct.completed.len(),
183-
description: temp_struct.task_description,
184-
id: id.clone(),
180+
num_completed: todo.completed.iter().filter(|b| **b).count(),
181+
num_tasks: todo.completed.len(),
182+
description: todo.description.clone(),
183+
id: todo.id.clone(),
185184
});
186185
}
187186
Ok(out)

crates/chat-cli/src/cli/chat/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ use crate::api_client::{
131131
};
132132
use crate::auth::AuthError;
133133
use crate::auth::builder_id::is_idc_user;
134+
use crate::cli::TodoState;
134135
use crate::cli::agent::Agents;
135136
use crate::cli::chat::cli::SlashCommand;
136137
use crate::cli::chat::cli::prompts::{
@@ -651,6 +652,11 @@ impl ChatSession {
651652
}
652653
});
653654

655+
// Create for cleaner error handling for todo lists
656+
// This is more of a convenience thing but is not required, so the Result
657+
// is ignored
658+
let _ = TodoState::init_dir(os).await;
659+
654660
Ok(Self {
655661
stdout,
656662
stderr,
@@ -2796,18 +2802,12 @@ impl ChatSession {
27962802
pub async fn resume_todo_request(&mut self, os: &mut Os, id: &str) -> Result<ChatState, ChatError> {
27972803
// Have to unpack each value separately since Reports can't be converted to
27982804
// ChatError
2799-
let todo_list_option = match os.database.get_todo(id) {
2800-
Ok(option) => option,
2805+
let todo_list = match TodoState::load(os, id).await {
2806+
Ok(todo) => todo,
28012807
Err(e) => {
2802-
return Err(ChatError::Custom(
2803-
format!("Error getting todo list from database: {e}").into(),
2804-
));
2808+
return Err(ChatError::Custom(format!("Error getting todo list: {e}").into()));
28052809
},
28062810
};
2807-
let todo_list = match todo_list_option {
2808-
Some(todo_list) => todo_list,
2809-
None => return Err(ChatError::Custom(format!("No todo list with id {}", id).into())),
2810-
};
28112811
let contents = match serde_json::to_string(&todo_list) {
28122812
Ok(s) => s,
28132813
Err(e) => return Err(ChatError::Custom(format!("Error deserializing todo list: {e}").into())),

0 commit comments

Comments
 (0)