cull_gmail/
message_list.rs

1use crate::{GmailClient, MessageSummary, Result};
2
3use google_gmail1::{
4    Gmail, api::ListMessagesResponse, hyper_rustls::HttpsConnector,
5    hyper_util::client::legacy::connect::HttpConnector,
6};
7
8/// Methods to select lists of messages from the mailbox
9pub trait MessageList {
10    /// Log the initial characters of the subjects of the message in the list
11    fn log_messages(&mut self) -> impl std::future::Future<Output = Result<()>> + Send;
12    /// List of messages
13    fn messages_list(
14        &mut self,
15        next_page_token: Option<String>,
16    ) -> impl std::future::Future<Output = Result<ListMessagesResponse>> + Send;
17    /// Run something
18    fn get_messages(&mut self, pages: u32) -> impl std::future::Future<Output = Result<()>> + Send;
19    /// Return the gmail hub
20    fn hub(&self) -> Gmail<HttpsConnector<HttpConnector>>;
21    /// Return the list of label_ids
22    fn label_ids(&self) -> Vec<String>;
23    /// Return the list of message ids
24    fn message_ids(&self) -> Vec<String>;
25    /// Return a summary of the messages (id and summary)
26    fn messages(&self) -> &Vec<MessageSummary>;
27    /// Set the query for the message list
28    fn set_query(&mut self, query: &str);
29    /// Add label ids to the list of labels for the message list
30    fn add_labels_ids(&mut self, label_ids: &[String]);
31    /// Add labels to the list of labels for the message list
32    fn add_labels(&mut self, labels: &[String]) -> Result<()>;
33    /// Report the max results value set
34    fn max_results(&self) -> u32;
35    /// Set the max_results value
36    fn set_max_results(&mut self, value: u32);
37}
38
39impl MessageList for GmailClient {
40    // /// Create a new List struct and add the Gmail api connection.
41    // async fn new(client: &GmailClient) -> Result<Self> {
42    //     Ok(MessageList {
43    //         hub: client.hub(),
44    //         max_results: DEFAULT_MAX_RESULTS.parse::<u32>().unwrap(),
45    //         label_ids: Vec::new(),
46    //         messages: Vec::new(),
47    //         query: String::new(),
48    //     })
49    // }
50
51    /// Set the maximum results
52    fn set_max_results(&mut self, value: u32) {
53        self.max_results = value;
54    }
55
56    /// Report the maximum results value
57    fn max_results(&self) -> u32 {
58        self.max_results
59    }
60
61    /// Add label to the labels collection
62    fn add_labels(&mut self, labels: &[String]) -> Result<()> {
63        log::debug!("labels from command line: {labels:?}");
64        let mut label_ids = Vec::new();
65        for label in labels {
66            if let Some(id) = self.get_label_id(label) {
67                label_ids.push(id)
68            }
69        }
70        self.add_labels_ids(label_ids.as_slice());
71        Ok(())
72    }
73
74    /// Add label to the labels collection
75    fn add_labels_ids(&mut self, label_ids: &[String]) {
76        if !label_ids.is_empty() {
77            for id in label_ids {
78                self.label_ids.push(id.to_string())
79            }
80        }
81    }
82
83    /// Set the query string
84    fn set_query(&mut self, query: &str) {
85        self.query = query.to_string()
86    }
87
88    /// Get the summary of the messages
89    fn messages(&self) -> &Vec<MessageSummary> {
90        &self.messages
91    }
92
93    /// Get a reference to the message_ids
94    fn message_ids(&self) -> Vec<String> {
95        self.messages
96            .iter()
97            .map(|m| m.id().to_string())
98            .collect::<Vec<_>>()
99            .clone()
100    }
101
102    /// Get a reference to the message_ids
103    fn label_ids(&self) -> Vec<String> {
104        self.label_ids.clone()
105    }
106
107    /// Get the hub
108    fn hub(&self) -> Gmail<HttpsConnector<HttpConnector>> {
109        self.hub().clone()
110    }
111
112    /// Run the Gmail api as configured
113    async fn get_messages(&mut self, pages: u32) -> Result<()> {
114        let list = self.messages_list(None).await?;
115        match pages {
116            1 => {}
117            0 => {
118                let mut list = list;
119                let mut page = 1;
120                loop {
121                    page += 1;
122                    log::debug!("Processing page #{page}");
123                    if list.next_page_token.is_none() {
124                        break;
125                    }
126                    list = self.messages_list(list.next_page_token).await?;
127                    // self.log_message_subjects(&list).await?;
128                }
129            }
130            _ => {
131                let mut list = list;
132                for page in 2..=pages {
133                    log::debug!("Processing page #{page}");
134                    if list.next_page_token.is_none() {
135                        break;
136                    }
137                    list = self.messages_list(list.next_page_token).await?;
138                    // self.log_message_subjects(&list).await?;
139                }
140            }
141        }
142
143        Ok(())
144    }
145
146    async fn messages_list(
147        &mut self,
148        next_page_token: Option<String>,
149    ) -> Result<ListMessagesResponse> {
150        let hub = self.hub();
151        let mut call = hub
152            .users()
153            .messages_list("me")
154            .max_results(self.max_results);
155        // Add any labels specified
156        if !self.label_ids.is_empty() {
157            log::debug!("Setting labels for list: {:#?}", self.label_ids);
158            for id in self.label_ids.as_slice() {
159                call = call.add_label_ids(id);
160            }
161        }
162        // Add query
163        if !self.query.is_empty() {
164            log::debug!("Setting query string `{}`", self.query);
165            call = call.q(&self.query);
166        }
167        // Add a page token if it exists
168        if let Some(page_token) = next_page_token {
169            log::debug!("Setting token for next page.");
170            call = call.page_token(&page_token);
171        }
172
173        let (_response, list) = call.doit().await.map_err(Box::new)?;
174        log::trace!(
175            "Estimated {} messages.",
176            list.result_size_estimate.unwrap_or(0)
177        );
178
179        if list.result_size_estimate.unwrap_or(0) == 0 {
180            log::warn!("Search returned no messages.");
181            return Ok(list);
182        }
183
184        let mut list_ids = list
185            .clone()
186            .messages
187            .unwrap()
188            .iter()
189            .flat_map(|item| item.id.as_ref().map(|id| MessageSummary::new(id)))
190            .collect();
191        self.messages.append(&mut list_ids);
192
193        Ok(list)
194    }
195
196    async fn log_messages(&mut self) -> Result<()> {
197        let hub = self.hub();
198        for message in &mut self.messages {
199            log::trace!("{}", message.id());
200            let (_res, m) = hub
201                .users()
202                .messages_get("me", message.id())
203                .add_scope("https://mail.google.com/")
204                .format("metadata")
205                .add_metadata_headers("subject")
206                .add_metadata_headers("date")
207                .doit()
208                .await
209                .map_err(Box::new)?;
210
211            let Some(payload) = m.payload else { continue };
212            let Some(headers) = payload.headers else {
213                continue;
214            };
215
216            for header in headers {
217                if let Some(name) = header.name {
218                    match name.to_lowercase().as_str() {
219                        "subject" => message.set_subject(header.value),
220                        "date" => message.set_date(header.value),
221                        _ => {}
222                    }
223                } else {
224                    continue;
225                }
226            }
227
228            log::info!("{}", message.list_date_and_subject());
229        }
230
231        Ok(())
232    }
233}