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