Add export-mbox command

lazy_fetch
Manos Pitsidianakis 2021-01-09 22:34:50 +02:00
parent 4050f6893f
commit a4ae4da8b1
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 99 additions and 0 deletions

View File

@ -418,6 +418,8 @@ Copy or move to other mailbox.
Copy or move to another account's mailbox. Copy or move to another account's mailbox.
.It Cm delete .It Cm delete
Delete selected threads. Delete selected threads.
.It Cm export-mbox Ar FILEPATH
Export selected threads to mboxcl2 file.
.It Cm create-mailbox Ar ACCOUNT Ar MAILBOX_PATH .It Cm create-mailbox Ar ACCOUNT Ar MAILBOX_PATH
create mailbox with given path. create mailbox with given path.
Be careful with backends and separator sensitivity (eg IMAP) Be careful with backends and separator sensitivity (eg IMAP)

View File

@ -419,6 +419,19 @@ define_commands!([
} }
) )
}, },
{ tags: ["export-mbox "],
desc: "export-mbox PATH",
tokens: &[One(Literal("export-mbox")), One(Filepath)],
parser:(
fn export_mbox(input: &[u8]) -> IResult<&[u8], Action> {
let (input, _) = tag("export-mbox")(input.trim())?;
let (input, _) = is_a(" ")(input)?;
let (input, path) = quoted_argument(input.trim())?;
let (input, _) = eof(input)?;
Ok((input, Listing(ExportMbox(Some(melib::backends::mbox::MboxFormat::MboxCl2), path.to_string().into()))))
}
)
},
{ tags: ["list-archive", "list-post", "list-unsubscribe", "list-"], { tags: ["list-archive", "list-post", "list-unsubscribe", "list-"],
desc: "list-[unsubscribe/post/archive]", desc: "list-[unsubscribe/post/archive]",
tokens: &[One(Alternatives(&[to_stream!(One(Literal("list-archive"))), to_stream!(One(Literal("list-post"))), to_stream!(One(Literal("list-unsubscribe")))]))], tokens: &[One(Alternatives(&[to_stream!(One(Literal("list-archive"))), to_stream!(One(Literal("list-post"))), to_stream!(One(Literal("list-unsubscribe")))]))],
@ -852,6 +865,7 @@ fn listing_action(input: &[u8]) -> IResult<&[u8], Action> {
select, select,
toggle_thread_snooze, toggle_thread_snooze,
open_in_new_tab, open_in_new_tab,
export_mbox,
_tag, _tag,
))(input) ))(input)
} }

View File

@ -51,6 +51,7 @@ pub enum ListingAction {
MoveTo(MailboxPath), MoveTo(MailboxPath),
MoveToOtherAccount(AccountName, MailboxPath), MoveToOtherAccount(AccountName, MailboxPath),
Import(PathBuf, MailboxPath), Import(PathBuf, MailboxPath),
ExportMbox(Option<melib::backends::mbox::MboxFormat>, PathBuf),
Delete, Delete,
OpenInNewTab, OpenInNewTab,
Tag(TagAction), Tag(TagAction),

View File

@ -342,6 +342,87 @@ pub trait MailListingTrait: ListingTrait {
} }
} }
} }
ListingAction::ExportMbox(format, ref path) => {
use futures::future::try_join_all;
use std::future::Future;
use std::io::Write;
use std::pin::Pin;
let futures: Result<Vec<_>> = envs_to_set
.iter()
.map(|&env_hash| account.operation(env_hash).and_then(|mut op| op.as_bytes()))
.collect::<Result<Vec<_>>>();
let path_ = path.to_path_buf();
let format = format.clone().unwrap_or_default();
let collection = account.collection.clone();
let (sender, mut receiver) = crate::jobs::oneshot::channel();
let fut: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>> =
Box::pin(async move {
let cl = async move {
let bytes: Vec<Vec<u8>> = try_join_all(futures?).await?;
let envs: Vec<_> = envs_to_set
.iter()
.map(|&env_hash| collection.get_env(env_hash))
.collect();
let mut file = std::io::BufWriter::new(std::fs::File::create(&path_)?);
let mut iter = envs.iter().zip(bytes.into_iter());
if let Some((env, ref bytes)) = iter.next() {
format.append(
&mut file,
bytes.as_slice(),
env.from().get(0),
Some(env.date()),
true,
false,
)?;
}
for (env, bytes) in iter {
format.append(
&mut file,
bytes.as_slice(),
env.from().get(0),
Some(env.date()),
false,
false,
)?;
}
file.flush()?;
Ok(())
};
let r: Result<()> = cl.await;
let _ = sender.send(r);
Ok(())
});
let handle = account.job_executor.spawn_blocking(fut);
let path = path.to_path_buf();
account.insert_job(
handle.job_id,
JobRequest::Generic {
name: "exporting mbox".into(),
handle,
on_finish: Some(CallbackFn(Box::new(move |context: &mut Context| {
context.replies.push_back(match receiver.try_recv() {
Err(_) | Ok(None) => UIEvent::Notification(
Some("Could not export mbox".to_string()),
"Job was canceled.".to_string(),
Some(NotificationType::Info),
),
Ok(Some(Err(err))) => UIEvent::Notification(
Some("Could not export mbox".to_string()),
err.to_string(),
Some(NotificationType::Error(err.kind)),
),
Ok(Some(Ok(()))) => UIEvent::Notification(
Some("Succesfully exported mbox".to_string()),
format!("Wrote to file {}", path.display()),
Some(NotificationType::Info),
),
});
}))),
logging_level: melib::LoggingLevel::INFO,
},
);
}
ListingAction::MoveToOtherAccount(ref _account_name, ref _mailbox_path) => { ListingAction::MoveToOtherAccount(ref _account_name, ref _mailbox_path) => {
context context
.replies .replies
@ -967,6 +1048,7 @@ impl Component for Listing {
| Action::Listing(a @ ListingAction::MoveTo(_)) | Action::Listing(a @ ListingAction::MoveTo(_))
| Action::Listing(a @ ListingAction::CopyToOtherAccount(_, _)) | Action::Listing(a @ ListingAction::CopyToOtherAccount(_, _))
| Action::Listing(a @ ListingAction::MoveToOtherAccount(_, _)) | Action::Listing(a @ ListingAction::MoveToOtherAccount(_, _))
| Action::Listing(a @ ListingAction::ExportMbox(_, _))
| Action::Listing(a @ ListingAction::Tag(_)) => { | Action::Listing(a @ ListingAction::Tag(_)) => {
let focused = self.component.get_focused_items(context); let focused = self.component.get_focused_items(context);
self.component.perform_action(context, focused, a); self.component.perform_action(context, focused, a);