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 list_messages(
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    /// Set the maximum results
41    fn set_max_results(&mut self, value: u32) {
42        self.max_results = value;
43    }
44
45    /// Report the maximum results value
46    fn max_results(&self) -> u32 {
47        self.max_results
48    }
49
50    /// Add label to the labels collection
51    fn add_labels(&mut self, labels: &[String]) -> Result<()> {
52        log::debug!("labels from command line: {labels:?}");
53        let mut label_ids = Vec::new();
54        for label in labels {
55            if let Some(id) = self.get_label_id(label) {
56                label_ids.push(id)
57            }
58        }
59        self.add_labels_ids(label_ids.as_slice());
60        Ok(())
61    }
62
63    /// Add label to the labels collection
64    fn add_labels_ids(&mut self, label_ids: &[String]) {
65        if !label_ids.is_empty() {
66            for id in label_ids {
67                self.label_ids.push(id.to_string())
68            }
69        }
70    }
71
72    /// Set the query string
73    fn set_query(&mut self, query: &str) {
74        self.query = query.to_string()
75    }
76
77    /// Get the summary of the messages
78    fn messages(&self) -> &Vec<MessageSummary> {
79        &self.messages
80    }
81
82    /// Get a reference to the message_ids
83    fn message_ids(&self) -> Vec<String> {
84        self.messages
85            .iter()
86            .map(|m| m.id().to_string())
87            .collect::<Vec<_>>()
88            .clone()
89    }
90
91    /// Get a reference to the message_ids
92    fn label_ids(&self) -> Vec<String> {
93        self.label_ids.clone()
94    }
95
96    /// Get the hub
97    fn hub(&self) -> Gmail<HttpsConnector<HttpConnector>> {
98        self.hub().clone()
99    }
100
101    /// Run the Gmail api as configured
102    async fn get_messages(&mut self, pages: u32) -> Result<()> {
103        let list = self.list_messages(None).await?;
104        match pages {
105            1 => {}
106            0 => {
107                let mut list = list;
108                let mut page = 1;
109                loop {
110                    page += 1;
111                    log::debug!("Processing page #{page}");
112                    if list.next_page_token.is_none() {
113                        break;
114                    }
115                    list = self.list_messages(list.next_page_token).await?;
116                    // self.log_message_subjects(&list).await?;
117                }
118            }
119            _ => {
120                let mut list = list;
121                for page in 2..=pages {
122                    log::debug!("Processing page #{page}");
123                    if list.next_page_token.is_none() {
124                        break;
125                    }
126                    list = self.list_messages(list.next_page_token).await?;
127                    // self.log_message_subjects(&list).await?;
128                }
129            }
130        }
131
132        Ok(())
133    }
134
135    async fn list_messages(
136        &mut self,
137        next_page_token: Option<String>,
138    ) -> Result<ListMessagesResponse> {
139        let hub = self.hub();
140        let mut call = hub
141            .users()
142            .messages_list("me")
143            .max_results(self.max_results);
144        // Add any labels specified
145        if !self.label_ids.is_empty() {
146            log::debug!("Setting labels for list: {:#?}", self.label_ids);
147            for id in self.label_ids.as_slice() {
148                call = call.add_label_ids(id);
149            }
150        }
151        // Add query
152        if !self.query.is_empty() {
153            log::debug!("Setting query string `{}`", self.query);
154            call = call.q(&self.query);
155        }
156        // Add a page token
157        if let Some(page_token) = next_page_token {
158            log::debug!("Setting token for next page.");
159            call = call.page_token(&page_token);
160        }
161
162        let (_response, list) = call.doit().await.map_err(Box::new)?;
163        log::trace!(
164            "Estimated {} messages.",
165            list.result_size_estimate.unwrap_or(0)
166        );
167
168        if list.result_size_estimate.unwrap_or(0) == 0 {
169            log::warn!("Search returned no messages.");
170            return Ok(list);
171        }
172
173        let mut list_ids = list
174            .clone()
175            .messages
176            .unwrap()
177            .iter()
178            .flat_map(|item| item.id.as_ref().map(|id| MessageSummary::new(id)))
179            .collect();
180        self.messages.append(&mut list_ids);
181
182        Ok(list)
183    }
184
185    async fn log_messages(&mut self) -> Result<()> {
186        let hub = self.hub();
187        for message in &mut self.messages {
188            log::trace!("{}", message.id());
189            let (_res, m) = hub
190                .users()
191                .messages_get("me", message.id())
192                .add_scope("https://mail.google.com/")
193                .format("metadata")
194                .add_metadata_headers("subject")
195                .add_metadata_headers("date")
196                .doit()
197                .await
198                .map_err(Box::new)?;
199
200            let Some(payload) = m.payload else { continue };
201            let Some(headers) = payload.headers else {
202                continue;
203            };
204
205            for header in headers {
206                if let Some(name) = header.name {
207                    match name.to_lowercase().as_str() {
208                        "subject" => message.set_subject(header.value),
209                        "date" => message.set_date(header.value),
210                        _ => {}
211                    }
212                } else {
213                    continue;
214                }
215            }
216
217            log::info!("{}", message.list_date_and_subject());
218        }
219
220        Ok(())
221    }
222}