mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 09:05:13 +00:00
save msg upon error (#59)
This commit is contained in:
parent
f4cc584716
commit
33185dba86
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Save msg upon error [#59]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- SMTP timeout [#87]
|
- SMTP timeout [#87]
|
||||||
|
@ -151,6 +155,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[#40]: https://github.com/soywod/himalaya/issues/40
|
[#40]: https://github.com/soywod/himalaya/issues/40
|
||||||
[#41]: https://github.com/soywod/himalaya/issues/41
|
[#41]: https://github.com/soywod/himalaya/issues/41
|
||||||
[#58]: https://github.com/soywod/himalaya/issues/58
|
[#58]: https://github.com/soywod/himalaya/issues/58
|
||||||
|
[#59]: https://github.com/soywod/himalaya/issues/59
|
||||||
[#61]: https://github.com/soywod/himalaya/issues/61
|
[#61]: https://github.com/soywod/himalaya/issues/61
|
||||||
[#71]: https://github.com/soywod/himalaya/issues/71
|
[#71]: https://github.com/soywod/himalaya/issues/71
|
||||||
[#74]: https://github.com/soywod/himalaya/issues/74
|
[#74]: https://github.com/soywod/himalaya/issues/74
|
||||||
|
|
153
src/input.rs
153
src/input.rs
|
@ -1,80 +1,167 @@
|
||||||
use error_chain::error_chain;
|
use error_chain::error_chain;
|
||||||
|
use log::{debug, error, trace};
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
fs::File,
|
fs::{self, File},
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
|
path::PathBuf,
|
||||||
process::Command,
|
process::Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
error_chain! {}
|
error_chain! {
|
||||||
|
foreign_links {
|
||||||
|
Utf8(std::string::FromUtf8Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draft_path() -> PathBuf {
|
||||||
|
env::temp_dir().join("himalaya-draft.mail")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_draft() -> Result<()> {
|
||||||
|
debug!("[input] remove draft");
|
||||||
|
|
||||||
|
let draft_path = draft_path();
|
||||||
|
debug!("[input] draft path: {:?}", draft_path);
|
||||||
|
|
||||||
|
fs::remove_file(&draft_path)
|
||||||
|
.chain_err(|| format!("Could not delete draft file {:?}", draft_path))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
|
pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
|
||||||
// Creates draft file
|
debug!("[input] open editor with tpl");
|
||||||
let mut draft_path = env::temp_dir();
|
trace!("{}", String::from_utf8(tpl.to_vec())?);
|
||||||
draft_path.push("himalaya-draft.mail");
|
|
||||||
File::create(&draft_path)
|
|
||||||
.chain_err(|| format!("Cannot create file `{}`", draft_path.to_string_lossy()))?
|
|
||||||
.write(tpl)
|
|
||||||
.chain_err(|| format!("Cannot write file `{}`", draft_path.to_string_lossy()))?;
|
|
||||||
|
|
||||||
// Opens editor and saves user input to draft file
|
let draft_path = draft_path();
|
||||||
Command::new(env::var("EDITOR").chain_err(|| "Cannot find `EDITOR` env var")?)
|
debug!("[input] draft path: {:?}", draft_path);
|
||||||
|
|
||||||
|
if draft_path.exists() {
|
||||||
|
debug!("[input] draft found");
|
||||||
|
loop {
|
||||||
|
match pre_edit_choice() {
|
||||||
|
Ok(choice) => match choice {
|
||||||
|
PreEditChoice::Edit => return open_editor_with_draft(),
|
||||||
|
PreEditChoice::Discard => break,
|
||||||
|
PreEditChoice::Quit => return Err("Edition aborted".into()),
|
||||||
|
},
|
||||||
|
Err(err) => error!("{}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("[input] create draft");
|
||||||
|
File::create(&draft_path)
|
||||||
|
.chain_err(|| format!("Could not create draft file {:?}", draft_path))?
|
||||||
|
.write(tpl)
|
||||||
|
.chain_err(|| format!("Could not write draft file {:?}", draft_path))?;
|
||||||
|
|
||||||
|
debug!("[input] open editor");
|
||||||
|
Command::new(env::var("EDITOR").chain_err(|| "Could not find `EDITOR` env var")?)
|
||||||
.arg(&draft_path)
|
.arg(&draft_path)
|
||||||
.status()
|
.status()
|
||||||
.chain_err(|| "Cannot start editor")?;
|
.chain_err(|| "Could not launch editor")?;
|
||||||
|
|
||||||
// Extracts draft file content
|
debug!("[input] read draft");
|
||||||
let mut draft = String::new();
|
let mut draft = String::new();
|
||||||
File::open(&draft_path)
|
File::open(&draft_path)
|
||||||
.chain_err(|| format!("Cannot open file `{}`", draft_path.to_string_lossy()))?
|
.chain_err(|| format!("Could not open draft file {:?}", draft_path))?
|
||||||
.read_to_string(&mut draft)
|
.read_to_string(&mut draft)
|
||||||
.chain_err(|| format!("Cannot read file `{}`", draft_path.to_string_lossy()))?;
|
.chain_err(|| format!("Could not read draft file {:?}", draft_path))?;
|
||||||
|
|
||||||
Ok(draft)
|
Ok(draft)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_editor_with_draft() -> Result<String> {
|
pub fn open_editor_with_draft() -> Result<String> {
|
||||||
// Creates draft file
|
debug!("[input] open editor with draft");
|
||||||
let mut draft_path = env::temp_dir();
|
|
||||||
draft_path.push("himalaya-draft.mail");
|
let draft_path = draft_path();
|
||||||
|
debug!("[input] draft path: {:?}", draft_path);
|
||||||
|
|
||||||
// Opens editor and saves user input to draft file
|
// Opens editor and saves user input to draft file
|
||||||
Command::new(env::var("EDITOR").chain_err(|| "Cannot find `EDITOR` env var")?)
|
Command::new(env::var("EDITOR").chain_err(|| "Could not find `EDITOR` env var")?)
|
||||||
.arg(&draft_path)
|
.arg(&draft_path)
|
||||||
.status()
|
.status()
|
||||||
.chain_err(|| "Cannot start editor")?;
|
.chain_err(|| "Could not launch editor")?;
|
||||||
|
|
||||||
// Extracts draft file content
|
// Extracts draft file content
|
||||||
let mut draft = String::new();
|
let mut draft = String::new();
|
||||||
File::open(&draft_path)
|
File::open(&draft_path)
|
||||||
.chain_err(|| format!("Cannot open file `{}`", draft_path.to_string_lossy()))?
|
.chain_err(|| format!("Could not open file {:?}", draft_path))?
|
||||||
.read_to_string(&mut draft)
|
.read_to_string(&mut draft)
|
||||||
.chain_err(|| format!("Cannot read file `{}`", draft_path.to_string_lossy()))?;
|
.chain_err(|| format!("Could not read file {:?}", draft_path))?;
|
||||||
|
|
||||||
Ok(draft)
|
Ok(draft)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Choice {
|
pub enum PreEditChoice {
|
||||||
Send,
|
|
||||||
Draft,
|
|
||||||
Edit,
|
Edit,
|
||||||
|
Discard,
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_edit_choice() -> Result<Choice> {
|
pub fn pre_edit_choice() -> Result<PreEditChoice> {
|
||||||
print!("(s)end, (d)raft, (e)dit or (q)uit? ");
|
debug!("[input] pre edit choice");
|
||||||
io::stdout().flush().chain_err(|| "Cannot flush stdout")?;
|
|
||||||
|
println!("A draft was found:");
|
||||||
|
print!("(e)dit, (d)iscard or (q)uit? ");
|
||||||
|
io::stdout()
|
||||||
|
.flush()
|
||||||
|
.chain_err(|| "Could not flush stdout")?;
|
||||||
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
io::stdin()
|
io::stdin()
|
||||||
.read_line(&mut buf)
|
.read_line(&mut buf)
|
||||||
.chain_err(|| "Cannot read stdin")?;
|
.chain_err(|| "Could not read stdin")?;
|
||||||
|
|
||||||
match buf.bytes().next().map(|bytes| bytes as char) {
|
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||||
Some('s') => Ok(Choice::Send),
|
Some('e') => {
|
||||||
Some('d') => Ok(Choice::Draft),
|
debug!("[input] pre edit choice: edit matched");
|
||||||
Some('e') => Ok(Choice::Edit),
|
Ok(PreEditChoice::Edit)
|
||||||
Some('q') => Ok(Choice::Quit),
|
}
|
||||||
|
Some('d') => {
|
||||||
|
debug!("[input] pre edit choice: discard matched");
|
||||||
|
Ok(PreEditChoice::Discard)
|
||||||
|
}
|
||||||
|
Some('q') => {
|
||||||
|
debug!("[input] pre edit choice: quit matched");
|
||||||
|
Ok(PreEditChoice::Quit)
|
||||||
|
}
|
||||||
|
Some(choice) => {
|
||||||
|
debug!("[input] pre edit choice: invalid choice {}", choice);
|
||||||
|
Err(format!("Invalid choice `{}`", choice).into())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug!("[input] pre edit choice: empty choice");
|
||||||
|
Err("Empty choice".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PostEditChoice {
|
||||||
|
Send,
|
||||||
|
Edit,
|
||||||
|
LocalDraft,
|
||||||
|
RemoteDraft,
|
||||||
|
Discard,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_edit_choice() -> Result<PostEditChoice> {
|
||||||
|
print!("(s)end, (e)dit, (l)ocal/(r)emote draft or (d)iscard? ");
|
||||||
|
io::stdout()
|
||||||
|
.flush()
|
||||||
|
.chain_err(|| "Could not flush stdout")?;
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
io::stdin()
|
||||||
|
.read_line(&mut buf)
|
||||||
|
.chain_err(|| "Could not read stdin")?;
|
||||||
|
|
||||||
|
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||||
|
Some('s') => Ok(PostEditChoice::Send),
|
||||||
|
Some('l') => Ok(PostEditChoice::LocalDraft),
|
||||||
|
Some('r') => Ok(PostEditChoice::RemoteDraft),
|
||||||
|
Some('e') => Ok(PostEditChoice::Edit),
|
||||||
|
Some('d') => Ok(PostEditChoice::Discard),
|
||||||
Some(choice) => Err(format!("Invalid choice `{}`", choice).into()),
|
Some(choice) => Err(format!("Invalid choice `{}`", choice).into()),
|
||||||
None => Err("Empty choice".into()),
|
None => Err("Empty choice".into()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,25 +302,31 @@ pub fn msg_matches(matches: &ArgMatches) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
match input::post_edit_choice() {
|
match input::post_edit_choice() {
|
||||||
Ok(choice) => match choice {
|
Ok(choice) => match choice {
|
||||||
input::Choice::Send => {
|
input::PostEditChoice::Send => {
|
||||||
debug!("Sending message…");
|
debug!("Sending message…");
|
||||||
let msg = msg.to_sendable_msg()?;
|
let msg = msg.to_sendable_msg()?;
|
||||||
smtp::send(&account, &msg)?;
|
smtp::send(&account, &msg)?;
|
||||||
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
||||||
|
input::remove_draft()?;
|
||||||
info!("Message successfully sent");
|
info!("Message successfully sent");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
input::Choice::Draft => {
|
input::PostEditChoice::Edit => {
|
||||||
debug!("Saving to draft…");
|
|
||||||
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
|
||||||
info!("Message successfully saved to Drafts");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
input::Choice::Edit => {
|
|
||||||
let content = input::open_editor_with_draft()?;
|
let content = input::open_editor_with_draft()?;
|
||||||
msg = Msg::from(content);
|
msg = Msg::from(content);
|
||||||
}
|
}
|
||||||
input::Choice::Quit => break,
|
input::PostEditChoice::LocalDraft => break,
|
||||||
|
input::PostEditChoice::RemoteDraft => {
|
||||||
|
debug!("Saving to draft…");
|
||||||
|
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
||||||
|
input::remove_draft()?;
|
||||||
|
info!("Message successfully saved to Drafts");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input::PostEditChoice::Discard => {
|
||||||
|
input::remove_draft()?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(err) => error!("{}", err),
|
Err(err) => error!("{}", err),
|
||||||
}
|
}
|
||||||
|
@ -400,25 +406,32 @@ pub fn msg_matches(matches: &ArgMatches) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
match input::post_edit_choice() {
|
match input::post_edit_choice() {
|
||||||
Ok(choice) => match choice {
|
Ok(choice) => match choice {
|
||||||
input::Choice::Send => {
|
input::PostEditChoice::Send => {
|
||||||
debug!("Sending message…");
|
debug!("Sending message…");
|
||||||
smtp::send(&account, &msg.to_sendable_msg()?)?;
|
let msg = msg.to_sendable_msg()?;
|
||||||
imap_conn.append_msg("Sent", &msg.to_vec()?, &[Flag::Seen])?;
|
smtp::send(&account, &msg)?;
|
||||||
|
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
||||||
imap_conn.add_flags(mbox, uid, "\\Answered")?;
|
imap_conn.add_flags(mbox, uid, "\\Answered")?;
|
||||||
|
input::remove_draft()?;
|
||||||
info!("Message successfully sent");
|
info!("Message successfully sent");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
input::Choice::Draft => {
|
input::PostEditChoice::Edit => {
|
||||||
debug!("Saving draft message…");
|
|
||||||
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
|
||||||
info!("Message successfully saved to Drafts");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
input::Choice::Edit => {
|
|
||||||
let content = input::open_editor_with_draft()?;
|
let content = input::open_editor_with_draft()?;
|
||||||
msg = Msg::from(content);
|
msg = Msg::from(content);
|
||||||
}
|
}
|
||||||
input::Choice::Quit => break,
|
input::PostEditChoice::LocalDraft => break,
|
||||||
|
input::PostEditChoice::RemoteDraft => {
|
||||||
|
debug!("Saving to draft…");
|
||||||
|
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
||||||
|
input::remove_draft()?;
|
||||||
|
info!("Message successfully saved to Drafts");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input::PostEditChoice::Discard => {
|
||||||
|
input::remove_draft()?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(err) => error!("{}", err),
|
Err(err) => error!("{}", err),
|
||||||
}
|
}
|
||||||
|
@ -449,24 +462,31 @@ pub fn msg_matches(matches: &ArgMatches) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
match input::post_edit_choice() {
|
match input::post_edit_choice() {
|
||||||
Ok(choice) => match choice {
|
Ok(choice) => match choice {
|
||||||
input::Choice::Send => {
|
input::PostEditChoice::Send => {
|
||||||
debug!("Sending message…");
|
debug!("Sending message…");
|
||||||
smtp::send(&account, &msg.to_sendable_msg()?)?;
|
let msg = msg.to_sendable_msg()?;
|
||||||
imap_conn.append_msg("Sent", &msg.to_vec()?, &[Flag::Seen])?;
|
smtp::send(&account, &msg)?;
|
||||||
|
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
||||||
|
input::remove_draft()?;
|
||||||
info!("Message successfully sent");
|
info!("Message successfully sent");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
input::Choice::Draft => {
|
input::PostEditChoice::Edit => {
|
||||||
debug!("Saving draft message…");
|
|
||||||
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
|
||||||
info!("Message successfully saved to Drafts");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
input::Choice::Edit => {
|
|
||||||
let content = input::open_editor_with_draft()?;
|
let content = input::open_editor_with_draft()?;
|
||||||
msg = Msg::from(content);
|
msg = Msg::from(content);
|
||||||
}
|
}
|
||||||
input::Choice::Quit => break,
|
input::PostEditChoice::LocalDraft => break,
|
||||||
|
input::PostEditChoice::RemoteDraft => {
|
||||||
|
debug!("Saving to draft…");
|
||||||
|
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
||||||
|
input::remove_draft()?;
|
||||||
|
info!("Message successfully saved to Drafts");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input::PostEditChoice::Discard => {
|
||||||
|
input::remove_draft()?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(err) => error!("{}", err),
|
Err(err) => error!("{}", err),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue