1use google_gmail1::{
2 Gmail,
3 api::ListMessagesResponse,
4 hyper_rustls::{HttpsConnector, HttpsConnectorBuilder},
5 hyper_util::{
6 client::legacy::{Client, connect::HttpConnector},
7 rt::TokioExecutor,
8 },
9 yup_oauth2::{ApplicationSecret, InstalledFlowAuthenticator, InstalledFlowReturnMethod},
10};
11
12use crate::{Credential, Error};
13
14pub const DEFAULT_MAX_RESULTS: &str = "10";
16
17pub struct List {
19 hub: Gmail<HttpsConnector<HttpConnector>>,
20 max_results: u32,
21 label_ids: Vec<String>,
22}
23
24impl List {
25 pub async fn new(credential: &str) -> Result<Self, Error> {
27 let (config_dir, secret) = {
28 let config_dir = crate::utils::assure_config_dir_exists("~/.cull-gmail")?;
29
30 let secret: ApplicationSecret = Credential::load_json_file(credential).into();
31 (config_dir, secret)
32 };
33
34 let executor = TokioExecutor::new();
35 let connector = HttpsConnectorBuilder::new()
36 .with_native_roots()
37 .unwrap()
38 .https_or_http()
39 .enable_http1()
40 .build();
41
42 let client = Client::builder(executor.clone()).build(connector.clone());
43
44 let auth = InstalledFlowAuthenticator::with_client(
45 secret,
46 InstalledFlowReturnMethod::HTTPRedirect,
47 Client::builder(executor).build(connector),
48 )
49 .persist_tokens_to_disk(format!("{config_dir}/gmail1"))
50 .build()
51 .await
52 .unwrap();
53
54 Ok(List {
55 hub: Gmail::new(client, auth),
56 max_results: DEFAULT_MAX_RESULTS.parse::<u32>().unwrap(),
57 label_ids: Vec::new(),
58 })
59 }
60
61 pub fn set_max_results(&mut self, value: u32) {
63 self.max_results = value;
64 }
65
66 pub fn max_results(&self) -> u32 {
68 self.max_results
69 }
70
71 pub fn add_label(&mut self, label_id: &str) {
73 self.label_ids.push(label_id.to_string())
74 }
75
76 pub async fn run(&self, pages: u32) -> Result<(), Error> {
78 let log_estimate = |est: &Option<u32>| {
79 if let Some(estimate) = est {
80 log::debug!("Estimated {estimate} messages in total.");
81 } else {
82 log::debug!("Unknown number of messages found");
83 }
84 };
85
86 let list = self.get_messages(None).await?;
87 log_estimate(&list.result_size_estimate);
88 self.log_message_subjects(&list).await?;
89 match pages {
90 1 => {}
91 0 => {
92 let mut list = list;
93 let mut page = 1;
94 loop {
95 page += 1;
96 log::debug!("Processing page #{page}");
97 log_estimate(&list.result_size_estimate);
98 if list.next_page_token.is_none() {
99 break;
100 }
101 list = self.get_messages(list.next_page_token).await?;
102 self.log_message_subjects(&list).await?;
103 }
104 }
105 _ => {
106 let mut list = list;
107 for page in 2..=pages {
108 log::debug!("Processing page #{page}");
109 log_estimate(&list.result_size_estimate);
110 if list.next_page_token.is_none() {
111 break;
112 }
113 list = self.get_messages(list.next_page_token).await?;
114 self.log_message_subjects(&list).await?;
115 }
116 }
117 }
118
119 Ok(())
120 }
121
122 async fn get_messages(
123 &self,
124 next_page_token: Option<String>,
125 ) -> Result<ListMessagesResponse, Error> {
126 let mut call = self
127 .hub
128 .users()
129 .messages_list("me")
130 .max_results(self.max_results);
131 if !self.label_ids.is_empty() {
133 log::debug!("Setting labels for list: {:#?}", self.label_ids);
134 for id in self.label_ids.as_slice() {
135 call = call.add_label_ids(id);
136 }
137 }
138 if let Some(page_token) = next_page_token {
140 log::debug!("Setting token for next page.");
141 call = call.page_token(&page_token);
142 }
143
144 let (_response, list) = call.doit().await.map_err(Box::new)?;
145
146 Ok(list)
147 }
148
149 async fn log_message_subjects(&self, list: &ListMessagesResponse) -> Result<(), Error> {
150 if let Some(messages) = &list.messages {
151 for message in messages {
152 if let Some(id) = &message.id {
153 log::trace!("{id}");
154 let (_res, m) = self
155 .hub
156 .users()
157 .messages_get("me", id)
158 .add_scope("https://www.googleapis.com/auth/gmail.metadata")
159 .format("metadata")
160 .add_metadata_headers("subject")
161 .doit()
162 .await
163 .map_err(Box::new)?;
164
165 let mut subject = String::new();
166 if let Some(payload) = m.payload {
167 if let Some(headers) = payload.headers {
168 for header in headers {
169 if header.name.is_some()
170 && header.name.unwrap() == "Subject"
171 && header.value.is_some()
172 {
173 subject = header.value.unwrap();
174 break;
175 } else {
176 continue;
177 }
178 }
179 }
180 }
181
182 log::info!("{subject:?}");
183 }
184 }
185 }
186
187 Ok(())
188 }
189}