/* * meli - maildir async * * Copyright 2020 Manos Pitsidianakis * * This file is part of meli. * * meli is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * meli is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ use std::{ future::Future, io::{self, Read}, path::PathBuf, pin::Pin, sync::{Arc, Mutex}, }; use futures::{ stream::{FuturesUnordered, StreamExt}, task::{Context, Poll}, }; use super::*; use crate::maildir::backend::move_to_cur; type Payload = Pin>> + Send + 'static>>; pub struct MaildirStream { payloads: Pin>>, } impl MaildirStream { #[allow(clippy::type_complexity, clippy::new_ret_no_self)] pub fn new( mailbox_hash: MailboxHash, unseen: Arc>, total: Arc>, mut path: PathBuf, map: HashIndexes, mailbox_index: Arc>>, ) -> Result>> + Send + 'static>>> { let chunk_size = 2048; path.push("new"); for p in path.read_dir()?.flatten() { move_to_cur(&p.path()).ok().take(); } path.pop(); path.push("cur"); let files: Vec = path .read_dir()? .flatten() .map(|e| e.path()) .collect::>(); let payloads = Box::pin(if !files.is_empty() { files .chunks(chunk_size) .map(|chunk| { Box::pin(Self::chunk( SmallVec::from(chunk), mailbox_hash, unseen.clone(), total.clone(), map.clone(), mailbox_index.clone(), )) as Pin + Send + 'static>> }) .collect::<_>() } else { FuturesUnordered::new() }); Ok(Self { payloads }.boxed()) } async fn chunk( chunk: SmallVec<[std::path::PathBuf; 2048]>, mailbox_hash: MailboxHash, unseen: Arc>, total: Arc>, map: HashIndexes, mailbox_index: Arc>>, ) -> Result> { let mut local_r: Vec = Vec::with_capacity(chunk.len()); let mut unseen_total: usize = 0; let mut buf = Vec::with_capacity(4096); for file in chunk { let env_hash = get_file_hash(&file); { map.lock() .unwrap() .entry(mailbox_hash) .or_default() .insert(env_hash, PathBuf::from(&file).into()); } let mut reader = io::BufReader::new(fs::File::open(&file)?); buf.clear(); reader.read_to_end(&mut buf)?; match Envelope::from_bytes(buf.as_slice(), Some(file.flags())) { Ok(mut env) => { env.set_hash(env_hash); mailbox_index.lock().unwrap().insert(env_hash, mailbox_hash); if !env.is_seen() { unseen_total += 1; } local_r.push(env); } Err(err) => { debug!( "DEBUG: hash {}, path: {} couldn't be parsed, {}", env_hash, file.as_path().display(), err, ); continue; } } } *total.lock().unwrap() += local_r.len(); *unseen.lock().unwrap() += unseen_total; Ok(local_r) } } impl Stream for MaildirStream { type Item = Result>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let payloads = self.payloads.as_mut(); payloads.poll_next(cx) } }