Skip to main content

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}