mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-22 11:00:19 +00:00
parent
130ed24a5a
commit
212f5e6eb1
18 changed files with 148 additions and 118 deletions
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improve `attachments` command [#281]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- `In-Reply-To` not set properly when replying to a message [#323]
|
- `In-Reply-To` not set properly when replying to a message [#323]
|
||||||
|
@ -475,6 +479,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[#271]: https://github.com/soywod/himalaya/issues/271
|
[#271]: https://github.com/soywod/himalaya/issues/271
|
||||||
[#276]: https://github.com/soywod/himalaya/issues/276
|
[#276]: https://github.com/soywod/himalaya/issues/276
|
||||||
[#280]: https://github.com/soywod/himalaya/issues/280
|
[#280]: https://github.com/soywod/himalaya/issues/280
|
||||||
|
[#281]: https://github.com/soywod/himalaya/issues/281
|
||||||
[#288]: https://github.com/soywod/himalaya/issues/288
|
[#288]: https://github.com/soywod/himalaya/issues/288
|
||||||
[#289]: https://github.com/soywod/himalaya/issues/289
|
[#289]: https://github.com/soywod/himalaya/issues/289
|
||||||
[#298]: https://github.com/soywod/himalaya/issues/298
|
[#298]: https://github.com/soywod/himalaya/issues/298
|
||||||
|
|
|
@ -26,10 +26,10 @@ impl Deref for ImapEnvelopes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintTable for ImapEnvelopes {
|
impl PrintTable for ImapEnvelopes {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Table::print(writter, self, opts)?;
|
Table::print(writer, self, opts)?;
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,10 @@ impl Deref for ImapMboxes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintTable for ImapMboxes {
|
impl PrintTable for ImapMboxes {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Table::print(writter, self, opts)?;
|
Table::print(writer, self, opts)?;
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,10 @@ impl DerefMut for MaildirEnvelopes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintTable for MaildirEnvelopes {
|
impl PrintTable for MaildirEnvelopes {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Table::print(writter, self, opts)?;
|
Table::print(writer, self, opts)?;
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,10 @@ impl Deref for MaildirMboxes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintTable for MaildirMboxes {
|
impl PrintTable for MaildirMboxes {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Table::print(writter, self, opts)?;
|
Table::print(writer, self, opts)?;
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,10 @@ impl DerefMut for NotmuchEnvelopes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintTable for NotmuchEnvelopes {
|
impl PrintTable for NotmuchEnvelopes {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Table::print(writter, self, opts)?;
|
Table::print(writer, self, opts)?;
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,10 @@ impl Deref for NotmuchMboxes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintTable for NotmuchMboxes {
|
impl PrintTable for NotmuchMboxes {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Table::print(writter, self, opts)?;
|
Table::print(writer, self, opts)?;
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,10 @@ impl Deref for Accounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintTable for Accounts {
|
impl PrintTable for Accounts {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Table::print(writter, self, opts)?;
|
Table::print(writer, self, opts)?;
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,11 +49,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn it_should_match_cmds_accounts() {
|
fn it_should_match_cmds_accounts() {
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
struct StringWritter {
|
struct StringWriter {
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl io::Write for StringWritter {
|
impl io::Write for StringWriter {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
self.content
|
self.content
|
||||||
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
||||||
|
@ -66,7 +66,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl termcolor::WriteColor for StringWritter {
|
impl termcolor::WriteColor for StringWriter {
|
||||||
fn supports_color(&self) -> bool {
|
fn supports_color(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -80,11 +80,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteColor for StringWritter {}
|
impl WriteColor for StringWriter {}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct PrinterServiceTest {
|
struct PrinterServiceTest {
|
||||||
pub writter: StringWritter,
|
pub writer: StringWriter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrinterService for PrinterServiceTest {
|
impl PrinterService for PrinterServiceTest {
|
||||||
|
@ -93,10 +93,16 @@ mod tests {
|
||||||
data: Box<T>,
|
data: Box<T>,
|
||||||
opts: PrintTableOpts,
|
opts: PrintTableOpts,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
data.print_table(&mut self.writter, opts)?;
|
data.print_table(&mut self.writer, opts)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn print<T: serde::Serialize + Print>(&mut self, _data: T) -> Result<()> {
|
fn print_str<T: Debug + Print>(&mut self, _data: T) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn print_struct<T: Debug + Print + serde::Serialize>(
|
||||||
|
&mut self,
|
||||||
|
_data: T,
|
||||||
|
) -> Result<()> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn is_json(&self) -> bool {
|
fn is_json(&self) -> bool {
|
||||||
|
@ -126,7 +132,7 @@ mod tests {
|
||||||
"account-1 │imap │yes \n",
|
"account-1 │imap │yes \n",
|
||||||
"\n"
|
"\n"
|
||||||
],
|
],
|
||||||
printer.writter.content
|
printer.writer.content
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,11 +47,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn it_should_list_mboxes() {
|
fn it_should_list_mboxes() {
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
struct StringWritter {
|
struct StringWriter {
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl io::Write for StringWritter {
|
impl io::Write for StringWriter {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
self.content
|
self.content
|
||||||
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
||||||
|
@ -64,7 +64,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl termcolor::WriteColor for StringWritter {
|
impl termcolor::WriteColor for StringWriter {
|
||||||
fn supports_color(&self) -> bool {
|
fn supports_color(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -78,11 +78,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteColor for StringWritter {}
|
impl WriteColor for StringWriter {}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct PrinterServiceTest {
|
struct PrinterServiceTest {
|
||||||
pub writter: StringWritter,
|
pub writer: StringWriter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrinterService for PrinterServiceTest {
|
impl PrinterService for PrinterServiceTest {
|
||||||
|
@ -91,10 +91,16 @@ mod tests {
|
||||||
data: Box<T>,
|
data: Box<T>,
|
||||||
opts: PrintTableOpts,
|
opts: PrintTableOpts,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
data.print_table(&mut self.writter, opts)?;
|
data.print_table(&mut self.writer, opts)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn print<T: serde::Serialize + Print>(&mut self, _data: T) -> Result<()> {
|
fn print_str<T: Debug + Print>(&mut self, _data: T) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn print_struct<T: Debug + Print + serde::Serialize>(
|
||||||
|
&mut self,
|
||||||
|
_data: T,
|
||||||
|
) -> Result<()> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn is_json(&self) -> bool {
|
fn is_json(&self) -> bool {
|
||||||
|
@ -181,7 +187,7 @@ mod tests {
|
||||||
"/ │Sent │NoInferiors, HasNoChildren \n",
|
"/ │Sent │NoInferiors, HasNoChildren \n",
|
||||||
"\n"
|
"\n"
|
||||||
],
|
],
|
||||||
printer.writter.content
|
printer.writer.content
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub fn add<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
backend: Box<&'a mut B>,
|
backend: Box<&'a mut B>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
backend.add_flags(mbox, seq_range, flags)?;
|
backend.add_flags(mbox, seq_range, flags)?;
|
||||||
printer.print(format!(
|
printer.print_struct(format!(
|
||||||
"Flag(s) {:?} successfully added to message(s) {:?}",
|
"Flag(s) {:?} successfully added to message(s) {:?}",
|
||||||
flags, seq_range
|
flags, seq_range
|
||||||
))
|
))
|
||||||
|
@ -32,7 +32,7 @@ pub fn remove<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
backend: Box<&'a mut B>,
|
backend: Box<&'a mut B>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
backend.del_flags(mbox, seq_range, flags)?;
|
backend.del_flags(mbox, seq_range, flags)?;
|
||||||
printer.print(format!(
|
printer.print_struct(format!(
|
||||||
"Flag(s) {:?} successfully removed from message(s) {:?}",
|
"Flag(s) {:?} successfully removed from message(s) {:?}",
|
||||||
flags, seq_range
|
flags, seq_range
|
||||||
))
|
))
|
||||||
|
@ -48,7 +48,7 @@ pub fn set<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
backend: Box<&'a mut B>,
|
backend: Box<&'a mut B>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
backend.set_flags(mbox, seq_range, flags)?;
|
backend.set_flags(mbox, seq_range, flags)?;
|
||||||
printer.print(format!(
|
printer.print_struct(format!(
|
||||||
"Flag(s) {:?} successfully set for message(s) {:?}",
|
"Flag(s) {:?} successfully set for message(s) {:?}",
|
||||||
flags, seq_range
|
flags, seq_range
|
||||||
))
|
))
|
||||||
|
|
|
@ -367,7 +367,7 @@ impl Msg {
|
||||||
.unwrap_or(DEFAULT_SENT_FOLDER);
|
.unwrap_or(DEFAULT_SENT_FOLDER);
|
||||||
backend.add_msg(&sent_folder, &sent_msg.formatted(), "seen")?;
|
backend.add_msg(&sent_folder, &sent_msg.formatted(), "seen")?;
|
||||||
msg_utils::remove_local_draft()?;
|
msg_utils::remove_local_draft()?;
|
||||||
printer.print("Message successfully sent")?;
|
printer.print_struct("Message successfully sent")?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(PostEditChoice::Edit) => {
|
Ok(PostEditChoice::Edit) => {
|
||||||
|
@ -375,7 +375,7 @@ impl Msg {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Ok(PostEditChoice::LocalDraft) => {
|
Ok(PostEditChoice::LocalDraft) => {
|
||||||
printer.print("Message successfully saved locally")?;
|
printer.print_struct("Message successfully saved locally")?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(PostEditChoice::RemoteDraft) => {
|
Ok(PostEditChoice::RemoteDraft) => {
|
||||||
|
@ -387,7 +387,8 @@ impl Msg {
|
||||||
.unwrap_or(DEFAULT_DRAFT_FOLDER);
|
.unwrap_or(DEFAULT_DRAFT_FOLDER);
|
||||||
backend.add_msg(&draft_folder, tpl.as_bytes(), "seen draft")?;
|
backend.add_msg(&draft_folder, tpl.as_bytes(), "seen draft")?;
|
||||||
msg_utils::remove_local_draft()?;
|
msg_utils::remove_local_draft()?;
|
||||||
printer.print(format!("Message successfully saved to {}", draft_folder))?;
|
printer
|
||||||
|
.print_struct(format!("Message successfully saved to {}", draft_folder))?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(PostEditChoice::Discard) => {
|
Ok(PostEditChoice::Discard) => {
|
||||||
|
|
|
@ -32,21 +32,29 @@ pub fn attachments<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let attachments = backend.get_msg(mbox, seq)?.attachments();
|
let attachments = backend.get_msg(mbox, seq)?.attachments();
|
||||||
let attachments_len = attachments.len();
|
let attachments_len = attachments.len();
|
||||||
debug!(
|
|
||||||
r#"{} attachment(s) found for message "{}""#,
|
if attachments_len == 0 {
|
||||||
attachments_len, seq
|
return printer.print_struct(format!("No attachment found for message {:?}", seq));
|
||||||
);
|
}
|
||||||
|
|
||||||
|
printer.print_str(format!(
|
||||||
|
"Found {:?} attachment{} for message {:?}",
|
||||||
|
attachments_len,
|
||||||
|
if attachments_len > 1 { "s" } else { "" },
|
||||||
|
seq
|
||||||
|
))?;
|
||||||
|
|
||||||
for attachment in attachments {
|
for attachment in attachments {
|
||||||
let file_path = config.get_download_file_path(&attachment.filename)?;
|
let file_path = config.get_download_file_path(&attachment.filename)?;
|
||||||
debug!("downloading {}…", attachment.filename);
|
printer.print_str(format!("Downloading {:?}…", file_path))?;
|
||||||
fs::write(&file_path, &attachment.content)
|
fs::write(&file_path, &attachment.content)
|
||||||
.context(format!("cannot download attachment {:?}", file_path))?;
|
.context(format!("cannot download attachment {:?}", file_path))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
printer.print(format!(
|
printer.print_struct(format!(
|
||||||
"{} attachment(s) successfully downloaded to {:?}",
|
"Attachment{} successfully downloaded to {:?}",
|
||||||
attachments_len, config.downloads_dir
|
if attachments_len > 1 { "s" } else { "" },
|
||||||
|
config.downloads_dir
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +67,7 @@ pub fn copy<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
backend: Box<&mut B>,
|
backend: Box<&mut B>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
backend.copy_msg(mbox_src, mbox_dst, seq)?;
|
backend.copy_msg(mbox_src, mbox_dst, seq)?;
|
||||||
printer.print(format!(
|
printer.print_struct(format!(
|
||||||
r#"Message {} successfully copied to folder "{}""#,
|
r#"Message {} successfully copied to folder "{}""#,
|
||||||
seq, mbox_dst
|
seq, mbox_dst
|
||||||
))
|
))
|
||||||
|
@ -73,7 +81,7 @@ pub fn delete<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
backend: Box<&'a mut B>,
|
backend: Box<&'a mut B>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
backend.del_msg(mbox, seq)?;
|
backend.del_msg(mbox, seq)?;
|
||||||
printer.print(format!(r#"Message(s) {} successfully deleted"#, seq))
|
printer.print_struct(format!(r#"Message(s) {} successfully deleted"#, seq))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forward the given message UID from the selected mailbox.
|
/// Forward the given message UID from the selected mailbox.
|
||||||
|
@ -189,7 +197,7 @@ pub fn move_<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
backend: Box<&'a mut B>,
|
backend: Box<&'a mut B>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
backend.move_msg(mbox_src, mbox_dst, seq)?;
|
backend.move_msg(mbox_src, mbox_dst, seq)?;
|
||||||
printer.print(format!(
|
printer.print_struct(format!(
|
||||||
r#"Message {} successfully moved to folder "{}""#,
|
r#"Message {} successfully moved to folder "{}""#,
|
||||||
seq, mbox_dst
|
seq, mbox_dst
|
||||||
))
|
))
|
||||||
|
@ -212,7 +220,7 @@ pub fn read<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
msg.fold_text_parts(text_mime)
|
msg.fold_text_parts(text_mime)
|
||||||
};
|
};
|
||||||
|
|
||||||
printer.print(msg)
|
printer.print_struct(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reply to the given message UID.
|
/// Reply to the given message UID.
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn new<'a, P: PrinterService>(
|
||||||
printer: &'a mut P,
|
printer: &'a mut P,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let tpl = Msg::default().to_tpl(opts, account)?;
|
let tpl = Msg::default().to_tpl(opts, account)?;
|
||||||
printer.print(tpl)
|
printer.print_struct(tpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a reply message template.
|
/// Generate a reply message template.
|
||||||
|
@ -38,7 +38,7 @@ pub fn reply<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
.get_msg(mbox, seq)?
|
.get_msg(mbox, seq)?
|
||||||
.into_reply(all, config)?
|
.into_reply(all, config)?
|
||||||
.to_tpl(opts, config)?;
|
.to_tpl(opts, config)?;
|
||||||
printer.print(tpl)
|
printer.print_struct(tpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a forward message template.
|
/// Generate a forward message template.
|
||||||
|
@ -54,7 +54,7 @@ pub fn forward<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
.get_msg(mbox, seq)?
|
.get_msg(mbox, seq)?
|
||||||
.into_forward(config)?
|
.into_forward(config)?
|
||||||
.to_tpl(opts, config)?;
|
.to_tpl(opts, config)?;
|
||||||
printer.print(tpl)
|
printer.print_struct(tpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Saves a message based on a template.
|
/// Saves a message based on a template.
|
||||||
|
@ -79,7 +79,7 @@ pub fn save<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
let msg = Msg::from_tpl(&tpl)?.add_attachments(attachments_paths)?;
|
let msg = Msg::from_tpl(&tpl)?.add_attachments(attachments_paths)?;
|
||||||
let raw_msg = msg.into_sendable_msg(config)?.formatted();
|
let raw_msg = msg.into_sendable_msg(config)?.formatted();
|
||||||
backend.add_msg(mbox, &raw_msg, "seen")?;
|
backend.add_msg(mbox, &raw_msg, "seen")?;
|
||||||
printer.print("Template successfully saved")
|
printer.print_struct("Template successfully saved")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a message based on a template.
|
/// Sends a message based on a template.
|
||||||
|
@ -105,5 +105,5 @@ pub fn send<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>(
|
||||||
let msg = Msg::from_tpl(&tpl)?.add_attachments(attachments_paths)?;
|
let msg = Msg::from_tpl(&tpl)?.add_attachments(attachments_paths)?;
|
||||||
let sent_msg = smtp.send_msg(account, &msg)?;
|
let sent_msg = smtp.send_msg(account, &msg)?;
|
||||||
backend.add_msg(mbox, &sent_msg.formatted(), "seen")?;
|
backend.add_msg(mbox, &sent_msg.formatted(), "seen")?;
|
||||||
printer.print("Template successfully sent")
|
printer.print_struct("Template successfully sent")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use log::error;
|
|
||||||
|
|
||||||
use crate::output::WriteColor;
|
use crate::output::WriteColor;
|
||||||
|
|
||||||
pub trait Print {
|
pub trait Print {
|
||||||
fn print(&self, writter: &mut dyn WriteColor) -> Result<()>;
|
fn print(&self, writer: &mut dyn WriteColor) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Print for &str {
|
impl Print for &str {
|
||||||
fn print(&self, writter: &mut dyn WriteColor) -> Result<()> {
|
fn print(&self, writer: &mut dyn WriteColor) -> Result<()> {
|
||||||
writeln!(writter, "{}", self).with_context(|| {
|
writeln!(writer, "{}", self).context("cannot write string to writer")
|
||||||
error!(r#"cannot write string to writter: "{}""#, self);
|
|
||||||
"cannot write string to writter"
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Print for String {
|
impl Print for String {
|
||||||
fn print(&self, writter: &mut dyn WriteColor) -> Result<()> {
|
fn print(&self, writer: &mut dyn WriteColor) -> Result<()> {
|
||||||
self.as_str().print(writter)
|
self.as_str().print(writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub trait WriteColor: io::Write + termcolor::WriteColor {}
|
||||||
impl WriteColor for StandardStream {}
|
impl WriteColor for StandardStream {}
|
||||||
|
|
||||||
pub trait PrintTable {
|
pub trait PrintTable {
|
||||||
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()>;
|
fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PrintTableOpts<'a> {
|
pub struct PrintTableOpts<'a> {
|
||||||
|
|
|
@ -9,8 +9,9 @@ use termcolor::{ColorChoice, StandardStream};
|
||||||
use crate::output::{OutputFmt, OutputJson, Print, PrintTable, PrintTableOpts, WriteColor};
|
use crate::output::{OutputFmt, OutputJson, Print, PrintTable, PrintTableOpts, WriteColor};
|
||||||
|
|
||||||
pub trait PrinterService {
|
pub trait PrinterService {
|
||||||
fn print<T: Debug + Print + serde::Serialize>(&mut self, data: T) -> Result<()>;
|
fn print_str<T: Debug + Print>(&mut self, data: T) -> Result<()>;
|
||||||
fn print_table<T: fmt::Debug + erased_serde::Serialize + PrintTable + ?Sized>(
|
fn print_struct<T: Debug + Print + serde::Serialize>(&mut self, data: T) -> Result<()>;
|
||||||
|
fn print_table<T: Debug + erased_serde::Serialize + PrintTable + ?Sized>(
|
||||||
&mut self,
|
&mut self,
|
||||||
data: Box<T>,
|
data: Box<T>,
|
||||||
opts: PrintTableOpts,
|
opts: PrintTableOpts,
|
||||||
|
@ -19,16 +20,23 @@ pub trait PrinterService {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StdoutPrinter {
|
pub struct StdoutPrinter {
|
||||||
pub writter: Box<dyn WriteColor>,
|
pub writer: Box<dyn WriteColor>,
|
||||||
pub fmt: OutputFmt,
|
pub fmt: OutputFmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrinterService for StdoutPrinter {
|
impl PrinterService for StdoutPrinter {
|
||||||
fn print<T: Debug + Print + serde::Serialize>(&mut self, data: T) -> Result<()> {
|
fn print_str<T: Debug + Print>(&mut self, data: T) -> Result<()> {
|
||||||
match self.fmt {
|
match self.fmt {
|
||||||
OutputFmt::Plain => data.print(self.writter.as_mut()),
|
OutputFmt::Plain => data.print(self.writer.as_mut()),
|
||||||
OutputFmt::Json => serde_json::to_writer(self.writter.as_mut(), &OutputJson::new(data))
|
OutputFmt::Json => Ok(()),
|
||||||
.context("cannot write JSON to writter"),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_struct<T: Debug + Print + serde::Serialize>(&mut self, data: T) -> Result<()> {
|
||||||
|
match self.fmt {
|
||||||
|
OutputFmt::Plain => data.print(self.writer.as_mut()),
|
||||||
|
OutputFmt::Json => serde_json::to_writer(self.writer.as_mut(), &OutputJson::new(data))
|
||||||
|
.context("cannot write JSON to writer"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +46,9 @@ impl PrinterService for StdoutPrinter {
|
||||||
opts: PrintTableOpts,
|
opts: PrintTableOpts,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match self.fmt {
|
match self.fmt {
|
||||||
OutputFmt::Plain => data.print_table(self.writter.as_mut(), opts),
|
OutputFmt::Plain => data.print_table(self.writer.as_mut(), opts),
|
||||||
OutputFmt::Json => {
|
OutputFmt::Json => {
|
||||||
let json = &mut serde_json::Serializer::new(self.writter.as_mut());
|
let json = &mut serde_json::Serializer::new(self.writer.as_mut());
|
||||||
let ser = &mut <dyn erased_serde::Serializer>::erase(json);
|
let ser = &mut <dyn erased_serde::Serializer>::erase(json);
|
||||||
data.erased_serialize(ser).unwrap();
|
data.erased_serialize(ser).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -55,7 +63,7 @@ impl PrinterService for StdoutPrinter {
|
||||||
|
|
||||||
impl From<OutputFmt> for StdoutPrinter {
|
impl From<OutputFmt> for StdoutPrinter {
|
||||||
fn from(fmt: OutputFmt) -> Self {
|
fn from(fmt: OutputFmt) -> Self {
|
||||||
let writter = StandardStream::stdout(if atty::isnt(Stream::Stdin) {
|
let writer = StandardStream::stdout(if atty::isnt(Stream::Stdin) {
|
||||||
// Colors should be deactivated if the terminal is not a tty.
|
// Colors should be deactivated if the terminal is not a tty.
|
||||||
ColorChoice::Never
|
ColorChoice::Never
|
||||||
} else {
|
} else {
|
||||||
|
@ -67,8 +75,8 @@ impl From<OutputFmt> for StdoutPrinter {
|
||||||
// [doc]: https://github.com/BurntSushi/termcolor#automatic-color-selection
|
// [doc]: https://github.com/BurntSushi/termcolor#automatic-color-selection
|
||||||
ColorChoice::Auto
|
ColorChoice::Auto
|
||||||
});
|
});
|
||||||
let writter = Box::new(writter);
|
let writer = Box::new(writer);
|
||||||
Self { writter, fmt }
|
Self { writer, fmt }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,14 +127,14 @@ impl Cell {
|
||||||
|
|
||||||
/// Makes the cell printable.
|
/// Makes the cell printable.
|
||||||
impl Print for Cell {
|
impl Print for Cell {
|
||||||
fn print(&self, writter: &mut dyn WriteColor) -> Result<()> {
|
fn print(&self, writer: &mut dyn WriteColor) -> Result<()> {
|
||||||
// Applies colors to the cell
|
// Applies colors to the cell
|
||||||
writter
|
writer
|
||||||
.set_color(&self.style)
|
.set_color(&self.style)
|
||||||
.context(format!(r#"cannot apply colors to cell "{}""#, self.value))?;
|
.context(format!(r#"cannot apply colors to cell "{}""#, self.value))?;
|
||||||
|
|
||||||
// Writes the colorized cell to stdout
|
// Writes the colorized cell to stdout
|
||||||
write!(writter, "{}", self.value).context(format!(r#"cannot print cell "{}""#, self.value))
|
write!(writer, "{}", self.value).context(format!(r#"cannot print cell "{}""#, self.value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,8 +167,8 @@ where
|
||||||
/// Defines the row template.
|
/// Defines the row template.
|
||||||
fn row(&self) -> Row;
|
fn row(&self) -> Row;
|
||||||
|
|
||||||
/// Writes the table to the writter.
|
/// Writes the table to the writer.
|
||||||
fn print(writter: &mut dyn WriteColor, items: &[Self], opts: PrintTableOpts) -> Result<()> {
|
fn print(writer: &mut dyn WriteColor, items: &[Self], opts: PrintTableOpts) -> Result<()> {
|
||||||
let is_format_flowed = matches!(opts.format, Format::Flowed);
|
let is_format_flowed = matches!(opts.format, Format::Flowed);
|
||||||
let max_width = match opts.format {
|
let max_width = match opts.format {
|
||||||
Format::Fixed(width) => opts.max_width.unwrap_or(*width),
|
Format::Fixed(width) => opts.max_width.unwrap_or(*width),
|
||||||
|
@ -202,7 +202,7 @@ where
|
||||||
for row in table.iter_mut() {
|
for row in table.iter_mut() {
|
||||||
let mut glue = Cell::default();
|
let mut glue = Cell::default();
|
||||||
for (i, cell) in row.0.iter_mut().enumerate() {
|
for (i, cell) in row.0.iter_mut().enumerate() {
|
||||||
glue.print(writter)?;
|
glue.print(writer)?;
|
||||||
|
|
||||||
let table_is_overflowing = table_width > max_width;
|
let table_is_overflowing = table_width > max_width;
|
||||||
if table_is_overflowing && !is_format_flowed && cell.is_shrinkable() {
|
if table_is_overflowing && !is_format_flowed && cell.is_shrinkable() {
|
||||||
|
@ -256,10 +256,10 @@ where
|
||||||
trace!("number of spaces added to value: {}", spaces_count);
|
trace!("number of spaces added to value: {}", spaces_count);
|
||||||
cell.value.push_str(&" ".repeat(spaces_count));
|
cell.value.push_str(&" ".repeat(spaces_count));
|
||||||
}
|
}
|
||||||
cell.print(writter)?;
|
cell.print(writer)?;
|
||||||
glue = Cell::new("│").ansi_256(8);
|
glue = Cell::new("│").ansi_256(8);
|
||||||
}
|
}
|
||||||
writeln!(writter)?;
|
writeln!(writer)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -272,11 +272,11 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct StringWritter {
|
struct StringWriter {
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl io::Write for StringWritter {
|
impl io::Write for StringWriter {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
self.content
|
self.content
|
||||||
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
||||||
|
@ -289,7 +289,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl termcolor::WriteColor for StringWritter {
|
impl termcolor::WriteColor for StringWriter {
|
||||||
fn supports_color(&self) -> bool {
|
fn supports_color(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -303,7 +303,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteColor for StringWritter {}
|
impl WriteColor for StringWriter {}
|
||||||
|
|
||||||
struct Item {
|
struct Item {
|
||||||
id: u16,
|
id: u16,
|
||||||
|
@ -338,16 +338,16 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! write_items {
|
macro_rules! write_items {
|
||||||
($writter:expr, $($item:expr),*) => {
|
($writer:expr, $($item:expr),*) => {
|
||||||
Table::print($writter, &[$($item,)*], PrintTableOpts { format: &Format::Auto, max_width: Some(20) }).unwrap();
|
Table::print($writer, &[$($item,)*], PrintTableOpts { format: &Format::Auto, max_width: Some(20) }).unwrap();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn row_smaller_than_head() {
|
fn row_smaller_than_head() {
|
||||||
let mut writter = StringWritter::default();
|
let mut writer = StringWriter::default();
|
||||||
write_items![
|
write_items![
|
||||||
&mut writter,
|
&mut writer,
|
||||||
Item::new(1, "a", "aa"),
|
Item::new(1, "a", "aa"),
|
||||||
Item::new(2, "b", "bb"),
|
Item::new(2, "b", "bb"),
|
||||||
Item::new(3, "c", "cc")
|
Item::new(3, "c", "cc")
|
||||||
|
@ -359,14 +359,14 @@ mod tests {
|
||||||
"2 │b │bb \n",
|
"2 │b │bb \n",
|
||||||
"3 │c │cc \n",
|
"3 │c │cc \n",
|
||||||
];
|
];
|
||||||
assert_eq!(expected, writter.content);
|
assert_eq!(expected, writer.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn row_bigger_than_head() {
|
fn row_bigger_than_head() {
|
||||||
let mut writter = StringWritter::default();
|
let mut writer = StringWriter::default();
|
||||||
write_items![
|
write_items![
|
||||||
&mut writter,
|
&mut writer,
|
||||||
Item::new(1, "a", "aa"),
|
Item::new(1, "a", "aa"),
|
||||||
Item::new(2222, "bbbbb", "bbbbb"),
|
Item::new(2222, "bbbbb", "bbbbb"),
|
||||||
Item::new(3, "c", "cc")
|
Item::new(3, "c", "cc")
|
||||||
|
@ -378,11 +378,11 @@ mod tests {
|
||||||
"2222 │bbbbb │bbbbb \n",
|
"2222 │bbbbb │bbbbb \n",
|
||||||
"3 │c │cc \n",
|
"3 │c │cc \n",
|
||||||
];
|
];
|
||||||
assert_eq!(expected, writter.content);
|
assert_eq!(expected, writer.content);
|
||||||
|
|
||||||
let mut writter = StringWritter::default();
|
let mut writer = StringWriter::default();
|
||||||
write_items![
|
write_items![
|
||||||
&mut writter,
|
&mut writer,
|
||||||
Item::new(1, "a", "aa"),
|
Item::new(1, "a", "aa"),
|
||||||
Item::new(2222, "bbbbb", "bbbbb"),
|
Item::new(2222, "bbbbb", "bbbbb"),
|
||||||
Item::new(3, "cccccc", "cc")
|
Item::new(3, "cccccc", "cc")
|
||||||
|
@ -394,14 +394,14 @@ mod tests {
|
||||||
"2222 │bbbbb │bbbbb \n",
|
"2222 │bbbbb │bbbbb \n",
|
||||||
"3 │cccccc │cc \n",
|
"3 │cccccc │cc \n",
|
||||||
];
|
];
|
||||||
assert_eq!(expected, writter.content);
|
assert_eq!(expected, writer.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_shrink() {
|
fn basic_shrink() {
|
||||||
let mut writter = StringWritter::default();
|
let mut writer = StringWriter::default();
|
||||||
write_items![
|
write_items![
|
||||||
&mut writter,
|
&mut writer,
|
||||||
Item::new(1, "", "desc"),
|
Item::new(1, "", "desc"),
|
||||||
Item::new(2, "short", "desc"),
|
Item::new(2, "short", "desc"),
|
||||||
Item::new(3, "loooooong", "desc"),
|
Item::new(3, "loooooong", "desc"),
|
||||||
|
@ -423,14 +423,14 @@ mod tests {
|
||||||
"7 │😍😍😍😍… │desc \n",
|
"7 │😍😍😍😍… │desc \n",
|
||||||
"8 │!😍😍😍… │desc \n",
|
"8 │!😍😍😍… │desc \n",
|
||||||
];
|
];
|
||||||
assert_eq!(expected, writter.content);
|
assert_eq!(expected, writer.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn max_shrink_width() {
|
fn max_shrink_width() {
|
||||||
let mut writter = StringWritter::default();
|
let mut writer = StringWriter::default();
|
||||||
write_items![
|
write_items![
|
||||||
&mut writter,
|
&mut writer,
|
||||||
Item::new(1111, "shriiiiiiiink", "desc very looong"),
|
Item::new(1111, "shriiiiiiiink", "desc very looong"),
|
||||||
Item::new(2222, "shriiiiiiiink", "desc very loooooooooong")
|
Item::new(2222, "shriiiiiiiink", "desc very loooooooooong")
|
||||||
];
|
];
|
||||||
|
@ -440,6 +440,6 @@ mod tests {
|
||||||
"1111 │shri… │desc very looong \n",
|
"1111 │shri… │desc very looong \n",
|
||||||
"2222 │shri… │desc very loooooooooong \n",
|
"2222 │shri… │desc very loooooooooong \n",
|
||||||
];
|
];
|
||||||
assert_eq!(expected, writter.content);
|
assert_eq!(expected, writer.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue