Переглянути джерело

1) removed some code duplications.
2) added limit and offset to sorted_threads_by_date api endpoint
3) When the program cannot download emails it continue to work as a archive without crashing

Yurii Sokolovskyi 2 місяців тому
батько
коміт
c20dec955b
3 змінених файлів з 38 додано та 81 видалено
  1. 18 60
      src/imap.rs
  2. 4 1
      src/main.rs
  3. 16 20
      src/server.rs

+ 18 - 60
src/imap.rs

@@ -57,15 +57,20 @@ pub async fn connect_to_imap() -> anyhow::Result<Client<TlsStream<TcpStream>>>{
     Ok(client)
 }
 
-/// download all emails from the server and persist them
-pub async fn download_email_from_imap() -> anyhow::Result<Vec<(u32, PathBuf)>>{
+async fn get_session() -> anyhow::Result<Session<TlsStream<TcpStream>>>{
     let mut client = connect_to_imap().await?;
     let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
-    let mut session = match session_result {
+    let session = match session_result {
         Ok(session) => {session}
         Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
     };
     
+    Ok(session)
+}
+
+/// download all emails from the server and persist them
+pub async fn download_email_from_imap() -> anyhow::Result<Vec<(u32, PathBuf)>>{    
+    let mut session = get_session().await?;
     let mut stored_paths: Vec<(u32, PathBuf)> = vec![];
     match list_imap_folders(&mut session).await { 
         Ok(folders) => {
@@ -246,14 +251,8 @@ fn update_local_uids(uid: String, list: String, uids_path: PathBuf) -> anyhow::R
 
 /// delete single message remotely by uid (marks deleted and deletes from the server at the same time)
 pub async fn delete_email_by_uid(list: String, uid: u32) -> anyhow::Result<()>{
-    // TODO split to mark_deleted() and expunge()
-    let mut client = connect_to_imap().await?;
-    let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
-    let mut session = match session_result {
-        Ok(session) => {session}
-        Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
-    };
-    
+    let mut session = get_session().await?;
+
     session.select(list.clone()).await?;
     let updates_stream = session.store(format!("{}", uid), "+FLAGS (\\Deleted)").await?;
     let _updates: Vec<_> = updates_stream.try_collect().await?;
@@ -265,12 +264,8 @@ pub async fn delete_email_by_uid(list: String, uid: u32) -> anyhow::Result<()>{
 
 /// create a folder remotely
 pub async fn create_folder(name: String) -> anyhow::Result<()>{
-    let mut client = connect_to_imap().await?;
-    let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
-    let mut session = match session_result {
-        Ok(session) => {session}
-        Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
-    };
+    let mut session = get_session().await?;
+
     session.create(name.clone()).await?;
     session.logout().await?;
     Ok(())
@@ -278,12 +273,8 @@ pub async fn create_folder(name: String) -> anyhow::Result<()>{
 
 /// rename a folder remotely
 pub async fn rename_folder(name: String, new_name: String) -> anyhow::Result<()>{
-    let mut client = connect_to_imap().await?;
-    let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
-    let mut session = match session_result {
-        Ok(session) => {session}
-        Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
-    };
+    let mut session = get_session().await?;
+
     session.rename(name.clone(), new_name.clone()).await?;
     session.logout().await?;
     Ok(())
@@ -291,12 +282,8 @@ pub async fn rename_folder(name: String, new_name: String) -> anyhow::Result<()>
 
 /// delete a folder remotely
 pub async fn delete_folder(name: String) -> anyhow::Result<()>{
-    let mut client = connect_to_imap().await?;
-    let mut session_result = client.login(Config::global().username.clone(), Config::global().password.clone()).await;
-    let mut session = match session_result {
-        Ok(session) => {session}
-        Err(_) => {return Err(anyhow!("Unable to login to IMAP server"))}
-    };
+    let mut session = get_session().await?;
+
     session.delete(name.clone()).await?;
     session.logout().await?;
     Ok(())
@@ -304,22 +291,7 @@ pub async fn delete_folder(name: String) -> anyhow::Result<()>{
 
 /// deletes all emails locally that were deleted remotely
 pub async fn remove_deleted_emails(mailbox: String) -> anyhow::Result<u32> {
-    // TODO move session creation to the function
-    let mut client = match connect_to_imap().await {
-        Ok(client) => client,
-        Err(e) => {
-            return Err(anyhow!("Failed to connect to IMAP"));
-        }
-    };
-
-    let session_result = client
-        .login(Config::global().username.clone(), Config::global().password.clone())
-        .await;
-
-    let mut session = match session_result {
-        Ok(session) => session,
-        Err(_) => return Err(anyhow!("Unable to login to IMAP server")),
-    };
+    let mut session = get_session().await?;
 
     session.select(mailbox.clone()).await?;
     
@@ -350,21 +322,7 @@ pub async fn remove_deleted_emails(mailbox: String) -> anyhow::Result<u32> {
 /// run a async task to monitor any changes in the mailbox 
 pub fn check_for_updates(mailbox: String) -> JoinHandle<Result<(), anyhow::Error>> {
     task::spawn(async move {
-        let mut client = match connect_to_imap().await {
-            Ok(client) => client,
-            Err(e) => {
-                return Err::<(), anyhow::Error>(anyhow!("Failed to connect to IMAP"));
-            }
-        };
-
-        let session_result = client
-            .login(Config::global().username.clone(), Config::global().password.clone())
-            .await;
-
-        let mut session = match session_result {
-            Ok(session) => session,
-            Err(_) => return Err(anyhow!("Unable to login to IMAP server")),
-        };
+        let mut session = get_session().await?;
 
         session.select(mailbox.clone()).await?;
 

+ 4 - 1
src/main.rs

@@ -392,7 +392,10 @@ async fn run(){
     });
     
     // downloading new emails
-    imap::download_email_from_imap().await.expect("Cannot download new emails");
+    match imap::download_email_from_imap().await {
+        Ok(_) => {}
+        Err(_) => println!("Cannot download new emails")
+    }
     
     // Uncomment to convert to HTML without downloading
     // let mut uid = 1; // fake uid for tests

+ 16 - 20
src/server.rs

@@ -144,21 +144,9 @@ pub async fn get_folders() -> String {
     serde_json::to_string(&Indexes::get_local_mailboxes()).unwrap()
 }
 
-pub async fn get_email(id: String) -> String { // TODO replace with Indexes::get_messages()
-    let messages_path = Config::global()
-        .out_dir
-        .clone()
-        .join("messages.json");
-    let messages_file = match File::open(messages_path) {
-        Ok(file) => file,
-        Err(_) => return String::new(),
-    };
-    let messages_reader = BufReader::new(messages_file);
-    let messages: Vec<SerializableMessage> = match serde_json::from_reader(messages_reader)
-    {
-        Ok(messages) => messages,
-        Err(_) => return String::new(),
-    };
+pub async fn get_email(id: String) -> String {
+    let messages = Indexes::get_messages().unwrap();
+    
     let message = match messages.iter().find(|message| message.id == id){
         None => {return String::new()}
         Some(message) => {message}
@@ -225,7 +213,7 @@ pub async fn get_thread(id: u32) -> String {
     }
 }
 
-async fn get_threads(folder: String, file_name: String) -> String {
+async fn get_threads(folder: String, limit: usize, offset: usize, file_name: String) -> String {
     let path = Config::global()
         .out_dir
         .clone()
@@ -242,12 +230,18 @@ async fn get_threads(folder: String, file_name: String) -> String {
         Ok(messages) => messages,
         Err(_) => return serde_json::to_string(&Vec::<(String, Vec<u32>)>::new()).unwrap(),
     };
+    
+    let result: Vec<(String, Vec<u32>)> = messages
+        .into_iter()
+        .skip(offset)
+        .take(limit)
+        .collect();
 
-    serde_json::to_string(&messages).unwrap()
+    serde_json::to_string(&result).unwrap()
 }
 
-pub async fn get_sorted_threads_by_date(folder: String) -> String {
-    get_threads(folder, "date.json".to_string()).await
+pub async fn get_sorted_threads_by_date(folder: String, limit: usize, offset: usize) -> String {
+    get_threads(folder, limit, offset, "date.json".to_string()).await
 }
 
 // Handlers
@@ -291,10 +285,12 @@ struct ErrorResponse {
 #[derive(Deserialize)]
 struct GetSortedThreadsByDateQuery {
     folder: String,
+    limit: usize,
+    offset: usize
 }
 
 async fn sorted_threads_by_date_handle(query: GetSortedThreadsByDateQuery) -> Result<impl warp::Reply, warp::Rejection> {
-    let result: Vec<(String, Vec<u32>)> = serde_json::from_str(&*get_sorted_threads_by_date(query.folder).await).unwrap();
+    let result: Vec<(String, Vec<u32>)> = serde_json::from_str(&*get_sorted_threads_by_date(query.folder, query.limit, query.offset).await).unwrap();
     Ok(warp::reply::json(&result))
 }