google_workspace_apis/tasks/requests.rs
1use anyhow::{anyhow, Error};
2use reqwest::Method;
3use serde::de::DeserializeOwned;
4
5use crate::{
6 auth::client::GoogleClient,
7 utils::request::{PaginationRequestTrait, Request},
8};
9
10use super::{
11 tasklist::types::TaskLists,
12 types::{Task, TaskLink, Tasks},
13};
14
15pub struct Uninitialized;
16pub struct TaskListMode;
17pub struct TaskInsertMode;
18pub struct TaskDeleteMode;
19pub struct TasksMode;
20pub struct TaskPatchMode;
21
22trait InitializedGetMode {}
23
24impl InitializedGetMode for TaskListMode {}
25impl InitializedGetMode for TasksMode {}
26
27pub trait TaskRequestBuilderTrait {
28 type TaskRequestBuilder;
29}
30
31pub struct TasksClient<'a, T = Uninitialized> {
32 request: Request<'a>,
33 task: Option<Task>,
34 _mode: std::marker::PhantomData<T>,
35}
36
37impl<'a> TasksClient<'a, Uninitialized> {
38 pub fn new(client: &'a mut GoogleClient) -> Self {
39 Self {
40 request: Request::new(client),
41 task: None,
42 _mode: std::marker::PhantomData,
43 }
44 }
45 /// Get a list of tasklists from the specified user.
46 ///
47 /// # Examples
48 ///
49 /// `Axum is used in this example, but it can be adapted to other frameworks like Actix or
50 /// Rocket.`
51 ///
52 ///``` rust
53 /// pub async fn get_tasks(State(state): State<AppState>,
54 /// Path((task_list_id): Path<(String, String) -> Json<TaskLists> {
55 /// let google_client_guard = state.google_client.lock().await;
56 /// let client = google_client_guard.as_ref().unwrap();
57 /// let res = TasksClient::new(client)
58 /// .get_tasks(&task_list_id)
59 /// .max_results(10)
60 /// .page_token(1)
61 /// .show_deleted(false)
62 /// .request()
63 /// .await.unwrap();
64 ///
65 /// Json(res.unwrap_or_default())
66 /// }
67 ///
68 pub fn get_task_lists(self) -> TasksClient<'a, TaskListMode> {
69 let mut builder = TasksClient {
70 request: self.request,
71 task: None,
72 _mode: std::marker::PhantomData,
73 };
74 builder.request.url = "https://tasks.googleapis.com/tasks/v1/users/@me/lists".to_string();
75 builder.request.method = reqwest::Method::GET;
76 builder
77 }
78
79 /// Get a list of tasks from the specified task list.
80 ///
81 /// # Examples
82 ///
83 /// `Axum is used in this example, but it can be adapted to other frameworks like Actix or
84 /// Rocket.`
85 ///
86 ///``` rust
87 /// pub async fn get_tasks(State(state): State<AppState>,
88 /// Path((task_list_id): Path<(String, String) -> Json<Tasks> {
89 /// let google_client_guard = state.google_client.lock().await;
90 /// let client = google_client_guard.as_ref().unwrap();
91 /// let res = TasksClient::new(client)
92 /// .get_tasks(&task_list_id)
93 /// .max_results(10)
94 /// .page_token(1)
95 /// .show_deleted(false)
96 /// .request()
97 /// .await.unwrap();
98 ///
99 /// Json(res.unwrap_or_default())
100 /// }
101 ///
102 pub fn get_tasks(self, task_list_id: &str) -> TasksClient<'a, TasksMode> {
103 let mut builder = TasksClient {
104 request: self.request,
105 task: None,
106 _mode: std::marker::PhantomData,
107 };
108 builder.request.url =
109 format!("https://tasks.googleapis.com/tasks/v1/lists/{task_list_id}/tasks");
110 builder.request.method = reqwest::Method::GET;
111 builder
112 }
113
114 /// Insert a task from the specified task list.
115 ///
116 /// # Examples
117 ///
118 /// `Axum is used in this example, but it can be adapted to other frameworks like Actix or
119 /// Rocket.`
120 ///
121 ///``` rust
122 /// pub async fn get_tasks(State(state): State<AppState>,
123 /// Path((task_id, task_list_id): Path<(String, String) {
124 /// let google_client_guard = state.google_client.lock().await;
125 /// let client = google_client_guard.as_ref().unwrap();
126 /// let res = TasksClient::new(client)
127 /// .insert_task(&task_list_id)
128 /// .set_task_notes("This is a new task")
129 /// .set_task_title("New Task")
130 /// .request()
131 /// .await.unwrap();
132 /// }
133 pub fn insert_task(self, task_list_id: &str) -> TasksClient<'a, TaskInsertMode> {
134 let mut builder = TasksClient {
135 request: self.request,
136 task: Some(Task::new()),
137 _mode: std::marker::PhantomData,
138 };
139 builder.request.url =
140 format!("https://tasks.googleapis.com/tasks/v1/lists/{task_list_id}/tasks");
141 builder.request.method = reqwest::Method::POST;
142 builder
143 }
144
145 /// Complete a task from the specified task list.
146 ///
147 /// # Examples
148 ///
149 /// `Axum is used in this example, but it can be adapted to other frameworks like Actix or
150 /// Rocket.`
151 ///
152 ///``` rust
153 /// pub async fn get_tasks(State(state): State<AppState>,
154 /// Path((task_id, task_list_id): Path<(String, String) {
155 /// let google_client_guard = state.google_client.lock().await;
156 /// let client = google_client_guard.as_ref().unwrap();
157 /// let res = TasksClient::new(client)
158 /// .complete_task(&task_list_id, &task_id)
159 /// .request()
160 /// .await.unwrap();
161 /// }
162 pub fn complete_task(
163 self,
164 task_id: &str,
165 task_list_id: &str,
166 ) -> TasksClient<'a, TaskPatchMode> {
167 let mut builder = TasksClient {
168 request: self.request,
169 task: None,
170 _mode: std::marker::PhantomData,
171 };
172 builder.request.url =
173 format!("https://tasks.googleapis.com/tasks/v1/lists/{task_list_id}/tasks/{task_id}");
174 builder.request.method = reqwest::Method::PATCH;
175 let payload = serde_json::json!({
176 "status": "completed"
177 });
178 builder.request.body = Some(serde_json::to_string(&payload).unwrap());
179 builder
180 }
181
182 /// Delete a task from the specified task list.
183 ///
184 /// # Examples
185 ///
186 /// `Axum is used in this example, but it can be adapted to other frameworks like Actix or
187 /// Rocket.`
188 ///
189 ///``` rust
190 /// pub async fn get_tasks(State(state): State<AppState>,
191 /// Path((task_id, task_list_id): Path<(String, String) {
192 /// let google_client_guard = state.google_client.lock().await;
193 /// let client = google_client_guard.as_ref().unwrap();
194 /// let res = TasksClient::new(client)
195 /// .delete_task(&task_list_id, &task_id)
196 /// .request()
197 /// .await.unwrap();
198 /// }
199 ///
200 pub fn delete_task(self, task_id: &str, task_list_id: &str) -> TasksClient<'a, TaskDeleteMode> {
201 let mut builder = TasksClient {
202 request: self.request,
203 task: None,
204 _mode: std::marker::PhantomData,
205 };
206 builder.request.url =
207 format!("https://tasks.googleapis.com/tasks/v1/lists/{task_list_id}/tasks/{task_id}");
208 builder.request.method = reqwest::Method::DELETE;
209 builder
210 }
211}
212
213impl<'a, T> TasksClient<'a, T> {
214 pub(super) async fn make_delete_request(&mut self) -> Result<bool, Error> {
215 self.request.client.refresh_access_token_check().await?;
216 let res = self
217 .request
218 .client
219 .req_client
220 .delete(&self.request.url)
221 .query(&self.request.params)
222 .send()
223 .await?;
224
225 if res.status().is_success() {
226 Ok(true)
227 } else {
228 Ok(false)
229 }
230 }
231 async fn make_request<R>(&mut self) -> Result<Option<R>, Error>
232 where
233 R: DeserializeOwned,
234 {
235 self.request.client.refresh_access_token_check().await?;
236 match self.request.method {
237 Method::GET => {
238 let res = self
239 .request
240 .client
241 .req_client
242 .get(&self.request.url)
243 .query(&self.request.params)
244 .send()
245 .await?;
246
247 if res.status().is_success() {
248 Ok(Some(res.json().await?))
249 } else {
250 Ok(None)
251 }
252 }
253
254 Method::POST => {
255 let res = self
256 .request
257 .client
258 .req_client
259 .post(&self.request.url)
260 .body(serde_json::to_string(&self.task).unwrap())
261 .query(&self.request.params)
262 .send()
263 .await?;
264
265 if res.status().is_success() {
266 Ok(Some(res.json().await?))
267 } else {
268 Ok(None)
269 }
270 }
271
272 Method::PATCH => {
273 let res = self
274 .request
275 .client
276 .req_client
277 .patch(&self.request.url)
278 .body(self.request.body.clone().unwrap_or_default())
279 .query(&self.request.params)
280 .send()
281 .await?;
282
283 if res.status().is_success() {
284 Ok(Some(res.json().await?))
285 } else {
286 Ok(None)
287 }
288 }
289 _ => Err(anyhow!("Unsupported HTTP method")),
290 }
291 }
292}
293
294impl<'a, T: InitializedGetMode> PaginationRequestTrait for TasksClient<'a, T> {
295 /// Sets the maximum number of results to return.
296 fn max_results(mut self, max: i64) -> Self {
297 self.request
298 .params
299 .insert("maxResults".to_string(), max.to_string());
300 self
301 }
302
303 /// Sets the page token for pagination. Works with `max_results`.
304 fn page_token(mut self, token: &str) -> Self {
305 self.request
306 .params
307 .insert("pageToken".to_string(), token.to_string());
308 self
309 }
310}
311
312impl<'a> TasksClient<'a, TaskListMode> {
313 /// Makes a request to retrieve the task lists.
314 pub async fn request(&mut self) -> Result<Option<TaskLists>, Error> {
315 self.make_request().await
316 }
317}
318
319/// A client for interacting with the Google Tasks API in retrieval mode.
320///
321/// This client allows querying task lists with various filtering options such as
322/// completion status, due dates, and visibility settings.
323///
324/// # Example
325/// ```
326/// let client = TasksClient::new(client);
327/// let tasks = client.show_completed(true).get_due_min(some_date).request().await?;
328/// ```
329impl<'a> TasksClient<'a, TasksMode> {
330 /// Makes a request to retrieve the tasks from the specified task list.
331 ///
332 /// # Returns
333 /// * `Result<Option<Tasks>, Error>` - A result containing the tasks if successful,
334 /// or an error if the request failed. Returns `None` if no tasks were found.
335 pub async fn request(&mut self) -> Result<Option<Tasks>, Error> {
336 self.make_request().await
337 }
338 /// Filter tasks by completion date to include only tasks completed before the specified date.
339 ///
340 /// # Arguments
341 /// * `completed_max` - The upper bound (exclusive) for a task's completion date to filter by
342 ///
343 /// # Returns
344 /// * `Self` - Returns the client for method chaining
345 pub fn get_completed_max(mut self, completed_max: chrono::DateTime<chrono::Utc>) -> Self {
346 self.request
347 .params
348 .insert("completedMax".to_string(), completed_max.to_string());
349 self
350 }
351
352 /// Filter tasks by completion date to include only tasks completed after the specified date.
353 ///
354 /// # Arguments
355 /// * `completed_min` - The lower bound (inclusive) for a task's completion date to filter by
356 ///
357 /// # Returns
358 /// * `Self` - Returns the client for method chaining
359 pub fn get_completed_min(mut self, completed_min: chrono::DateTime<chrono::Utc>) -> Self {
360 self.request
361 .params
362 .insert("completedMin".to_string(), completed_min.to_string());
363 self
364 }
365
366 /// Filter tasks by due date to include only tasks due before the specified date.
367 ///
368 /// # Arguments
369 /// * `due_max` - The upper bound (exclusive) for a task's due date to filter by
370 ///
371 /// # Returns
372 /// * `Self` - Returns the client for method chaining
373 pub fn get_due_max(mut self, due_max: chrono::DateTime<chrono::Utc>) -> Self {
374 self.request
375 .params
376 .insert("dueMax".to_string(), due_max.to_string());
377 self
378 }
379
380 /// Filter tasks by due date to include only tasks due after the specified date.
381 ///
382 /// # Arguments
383 /// * `due_min` - The lower bound (inclusive) for a task's due date to filter by
384 ///
385 /// # Returns
386 /// * `Self` - Returns the client for method chaining
387 pub fn get_due_min(mut self, due_min: chrono::DateTime<chrono::Utc>) -> Self {
388 self.request
389 .params
390 .insert("dueMin".to_string(), due_min.to_string());
391 self
392 }
393
394 /// Control whether completed tasks are included in the result.
395 ///
396 /// # Arguments
397 /// * `show_completed` - If true, completed tasks are included in the result
398 ///
399 /// # Returns
400 /// * `Self` - Returns the client for method chaining
401 pub fn show_completed(mut self, show_completed: bool) -> Self {
402 self.request
403 .params
404 .insert("showCompleted".to_string(), show_completed.to_string());
405 self
406 }
407
408 /// Control whether deleted tasks are included in the result.
409 ///
410 /// # Arguments
411 /// * `show_deleted` - If true, deleted tasks are included in the result
412 ///
413 /// # Returns
414 /// * `Self` - Returns the client for method chaining
415 pub fn show_deleted(mut self, show_due: bool) -> Self {
416 self.request
417 .params
418 .insert("showDeleted".to_string(), show_due.to_string());
419 self
420 }
421
422 /// Control whether hidden tasks are included in the result.
423 ///
424 /// # Arguments
425 /// * `show_hidden` - If true, hidden tasks are included in the result
426 ///
427 /// # Returns
428 /// * `Self` - Returns the client for method chaining
429 pub fn show_hidden(mut self, show_hidden: bool) -> Self {
430 self.request
431 .params
432 .insert("showHidden".to_string(), show_hidden.to_string());
433 self
434 }
435
436 /// Filter tasks to include only those updated after the specified time.
437 ///
438 /// # Arguments
439 /// * `updated_min` - The minimum last updated date (inclusive) to filter by
440 ///
441 /// # Returns
442 /// * `Self` - Returns the client for method chaining
443 pub fn get_updated_min(mut self, updated_min: chrono::DateTime<chrono::Utc>) -> Self {
444 self.request
445 .params
446 .insert("updatedMin".to_string(), updated_min.to_string());
447 self
448 }
449
450 /// Control whether assigned tasks are included in the result.
451 ///
452 /// # Arguments
453 /// * `show_assigned` - If true, assigned tasks are included in the result
454 ///
455 /// # Returns
456 /// * `Self` - Returns the client for method chaining
457 pub fn show_assigned(mut self, show_assigned: bool) -> Self {
458 self.request
459 .params
460 .insert("showAssigned".to_string(), show_assigned.to_string());
461 self
462 }
463}
464
465/// A client for interacting with the Google Tasks API in task insertion mode.
466///
467/// This client allows creating new tasks with various properties such as
468/// title, notes, due dates, and hierarchical placement.
469///
470/// # Example
471/// ```
472/// let client = TasksClient::new(client);
473/// let task = client.set_task_title("New Task").set_task_notes("Details").request().await?;
474/// ```
475impl<'a> TasksClient<'a, TaskInsertMode> {
476 /// Makes a request to create a task with the specified properties.
477 ///
478 /// # Returns
479 /// * `Result<Option<Tasks>, Error>` - A result containing the created task if successful,
480 /// or an error if the request failed.
481 pub async fn request(&mut self) -> Result<Option<Tasks>, Error> {
482 self.make_request().await
483 }
484
485 /// Sets the parent task for this task, establishing a hierarchical relationship.
486 ///
487 /// # Arguments
488 /// * `parent_id` - The ID of the parent task
489 ///
490 /// # Returns
491 /// * `Self` - Returns the client for method chaining
492 pub fn set_parent(mut self, parent_id: &str) -> Self {
493 self.request
494 .params
495 .insert("parent".to_string(), parent_id.to_string());
496 self
497 }
498
499 /// Sets the previous sibling task for this task, establishing the order of tasks.
500 ///
501 /// # Arguments
502 /// * `previous_id` - The ID of the previous task in the sequence
503 ///
504 /// # Returns
505 /// * `Self` - Returns the client for method chaining
506 pub fn set_previous(mut self, previous_id: &str) -> Self {
507 self.request
508 .params
509 .insert("previous".to_string(), previous_id.to_string());
510 self
511 }
512
513 /// Sets the complete task object to be inserted.
514 ///
515 /// # Arguments
516 /// * `task` - The Task object containing all properties to be set
517 ///
518 /// # Returns
519 /// * `Self` - Returns the client for method chaining
520 pub fn set_task(mut self, task: Task) -> Self {
521 self.task = Some(task);
522 self
523 }
524
525 /// Sets the title of the task to be created.
526 ///
527 /// # Arguments
528 /// * `title` - The title for the task
529 ///
530 /// # Returns
531 /// * `Self` - Returns the client for method chaining
532 pub fn set_task_title(self, title: &str) -> Self {
533 self.modify_task(|task| task.title = title.to_string())
534 }
535
536 /// Sets the ETag of the task to be created.
537 ///
538 /// # Arguments
539 /// * `etag` - The ETag for the task
540 ///
541 /// # Returns
542 /// * `Self` - Returns the client for method chaining
543 pub fn set_task_etag(self, etag: &str) -> Self {
544 self.modify_task(|task| task.etag = etag.to_string())
545 }
546
547 /// Sets the notes describing the task.
548 ///
549 /// # Arguments
550 /// * `notes` - The notes for the task (max 8192 characters)
551 ///
552 /// # Returns
553 /// * `Self` - Returns the client for method chaining
554 pub fn set_task_notes(self, notes: &str) -> Self {
555 self.modify_task(|event| event.notes = notes.to_string())
556 }
557
558 /// Sets the due date of the task.
559 ///
560 /// # Arguments
561 /// * `due` - The due date for the task (as a DateTime)
562 ///
563 /// # Returns
564 /// * `Self` - Returns the client for method chaining
565 pub fn set_task_due(self, due: chrono::DateTime<chrono::Utc>) -> Self {
566 self.modify_task(|task| task.due = Some(due))
567 }
568
569 /// Sets the completion date of the task.
570 ///
571 /// # Arguments
572 /// * `completed` - The datetime when the task was completed
573 ///
574 /// # Returns
575 /// * `Self` - Returns the client for method chaining
576 pub fn set_task_completed(self, completed: chrono::DateTime<chrono::Utc>) -> Self {
577 self.modify_task(|task| task.completed = Some(completed))
578 }
579
580 /// Sets the hidden status of the task.
581 ///
582 /// # Arguments
583 /// * `hidden` - If true, marks the task as hidden
584 ///
585 /// # Returns
586 /// * `Self` - Returns the client for method chaining
587 pub fn set_task_hidden(self, hidden: bool) -> Self {
588 self.modify_task(|task| task.hidden = hidden)
589 }
590
591 /// Sets the links associated with the task.
592 ///
593 /// # Arguments
594 /// * `links` - A vector of TaskLink objects to associate with the task
595 ///
596 /// # Returns
597 /// * `Self` - Returns the client for method chaining
598 pub fn set_task_links(self, links: Vec<TaskLink>) -> Self {
599 self.modify_task(|task| task.links = links)
600 }
601
602 fn modify_task<F>(mut self, modifier: F) -> Self
603 where
604 F: FnOnce(&mut Task),
605 {
606 if let Some(ref mut task) = self.task {
607 modifier(task);
608 }
609 self
610 }
611}
612
613impl<'a> TasksClient<'a, TaskPatchMode> {
614 /// Makes a request to update the task with the specified properties.
615 ///
616 /// # Returns
617 /// * `Result<Option<Tasks>, Error>` - A result containing the updated task if successful,
618 /// or an error if the request failed.
619 pub async fn request(&mut self) -> Result<Option<Task>, Error> {
620 self.make_request().await
621 }
622}
623
624impl<'a> TasksClient<'a, TaskDeleteMode> {
625 /// Makes a request to delete the specified task.
626 ///
627 /// # Returns
628 /// * `Result<bool, Error>` - A result indicating whether the deletion was successful.
629 pub async fn request(&mut self) -> Result<bool, Error> {
630 self.make_delete_request().await
631 }
632}