cull_gmail/
message_list.rs1use crate::{GmailClient, MessageSummary, Result};
2
3use google_gmail1::{
4 Gmail, api::ListMessagesResponse, hyper_rustls::HttpsConnector,
5 hyper_util::client::legacy::connect::HttpConnector,
6};
7
8use crate::utils::Elide;
9
10pub trait MessageList {
12 fn log_message_subjects(&mut self) -> impl std::future::Future<Output = Result<()>> + Send;
14 fn messages_list(
16 &mut self,
17 next_page_token: Option<String>,
18 ) -> impl std::future::Future<Output = Result<ListMessagesResponse>> + Send;
19 fn get_messages(&mut self, pages: u32) -> impl std::future::Future<Output = Result<()>> + Send;
21 fn hub(&self) -> Gmail<HttpsConnector<HttpConnector>>;
23 fn label_ids(&self) -> Vec<String>;
25 fn message_ids(&self) -> Vec<String>;
27 fn messages(&self) -> &Vec<MessageSummary>;
29 fn set_query(&mut self, query: &str);
31 fn add_labels_ids(&mut self, label_ids: &[String]);
33 fn add_labels(&mut self, labels: &[String]) -> Result<()>;
35 fn max_results(&self) -> u32;
37 fn set_max_results(&mut self, value: u32);
39}
40
41impl MessageList for GmailClient {
42 fn set_max_results(&mut self, value: u32) {
55 self.max_results = value;
56 }
57
58 fn max_results(&self) -> u32 {
60 self.max_results
61 }
62
63 fn add_labels(&mut self, labels: &[String]) -> Result<()> {
65 log::debug!("labels from command line: {labels:?}");
66 let mut label_ids = Vec::new();
67 for label in labels {
68 if let Some(id) = self.get_label_id(label) {
69 label_ids.push(id)
70 }
71 }
72 self.add_labels_ids(label_ids.as_slice());
73 Ok(())
74 }
75
76 fn add_labels_ids(&mut self, label_ids: &[String]) {
78 if !label_ids.is_empty() {
79 for id in label_ids {
80 self.label_ids.push(id.to_string())
81 }
82 }
83 }
84
85 fn set_query(&mut self, query: &str) {
87 self.query = query.to_string()
88 }
89
90 fn messages(&self) -> &Vec<MessageSummary> {
92 &self.messages
93 }
94
95 fn message_ids(&self) -> Vec<String> {
97 self.messages
98 .iter()
99 .map(|m| m.id().to_string())
100 .collect::<Vec<_>>()
101 .clone()
102 }
103
104 fn label_ids(&self) -> Vec<String> {
106 self.label_ids.clone()
107 }
108
109 fn hub(&self) -> Gmail<HttpsConnector<HttpConnector>> {
111 self.hub().clone()
112 }
113
114 async fn get_messages(&mut self, pages: u32) -> Result<()> {
116 let list = self.messages_list(None).await?;
117 match pages {
118 1 => {}
119 0 => {
120 let mut list = list;
121 let mut page = 1;
122 loop {
123 page += 1;
124 log::debug!("Processing page #{page}");
125 if list.next_page_token.is_none() {
126 break;
127 }
128 list = self.messages_list(list.next_page_token).await?;
129 }
131 }
132 _ => {
133 let mut list = list;
134 for page in 2..=pages {
135 log::debug!("Processing page #{page}");
136 if list.next_page_token.is_none() {
137 break;
138 }
139 list = self.messages_list(list.next_page_token).await?;
140 }
142 }
143 }
144
145 if log::max_level() >= log::Level::Info {
146 self.log_message_subjects().await?;
147 }
148
149 Ok(())
150 }
151
152 async fn messages_list(
153 &mut self,
154 next_page_token: Option<String>,
155 ) -> Result<ListMessagesResponse> {
156 let hub = self.hub();
157 let mut call = hub
158 .users()
159 .messages_list("me")
160 .max_results(self.max_results);
161 if !self.label_ids.is_empty() {
163 log::debug!("Setting labels for list: {:#?}", self.label_ids);
164 for id in self.label_ids.as_slice() {
165 call = call.add_label_ids(id);
166 }
167 }
168 if !self.query.is_empty() {
170 log::debug!("Setting query string `{}`", self.query);
171 call = call.q(&self.query);
172 }
173 if let Some(page_token) = next_page_token {
175 log::debug!("Setting token for next page.");
176 call = call.page_token(&page_token);
177 }
178
179 let (_response, list) = call.doit().await.map_err(Box::new)?;
180 log::trace!(
181 "Estimated {} messages.",
182 list.result_size_estimate.unwrap_or(0)
183 );
184
185 if list.result_size_estimate.unwrap_or(0) == 0 {
186 log::warn!("Search returned no messages.");
187 return Ok(list);
188 }
189
190 let mut list_ids = list
191 .clone()
192 .messages
193 .unwrap()
194 .iter()
195 .flat_map(|item| item.id.as_ref().map(|id| MessageSummary::new(id)))
196 .collect();
197 self.messages.append(&mut list_ids);
198
199 Ok(list)
200 }
201
202 async fn log_message_subjects(&mut self) -> Result<()> {
203 let hub = self.hub();
204 for message in &mut self.messages {
205 log::trace!("{}", message.id());
206 let (_res, m) = hub
207 .users()
208 .messages_get("me", message.id())
209 .add_scope("https://www.googleapis.com/auth/gmail.metadata")
210 .format("metadata")
211 .add_metadata_headers("subject")
212 .doit()
213 .await
214 .map_err(Box::new)?;
215
216 let mut subject = String::new();
217 let Some(payload) = m.payload else { continue };
218 let Some(headers) = payload.headers else {
219 continue;
220 };
221
222 for header in headers {
223 if header.name.is_some()
224 && header.name.unwrap() == "Subject"
225 && header.value.is_some()
226 {
227 subject = header.value.unwrap();
228 break;
229 } else {
230 continue;
231 }
232 }
233
234 if subject.is_empty() {
235 log::info!("***Email with no subject***");
236 } else {
237 subject.elide(24);
238 message.set_subject(&subject);
239 log::info!("{subject:?}");
240 }
241 }
242
243 Ok(())
244 }
245}