implement flowed format POC (#206)

This commit is contained in:
Clément DOUIN 2022-03-03 12:16:59 +01:00
parent 6e5362e76e
commit 613e592f72
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
10 changed files with 109 additions and 35 deletions

View file

@ -29,6 +29,9 @@ pub struct AccountConfig {
pub notify_query: String,
/// Represents the watch commands.
pub watch_cmds: Vec<String>,
/// Represents the text/plain format as defined in the
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
pub format: Format,
/// Represents mailbox aliases.
pub mailboxes: HashMap<String, String>,
@ -148,6 +151,7 @@ impl<'a> AccountConfig {
.or_else(|| config.watch_cmds.as_ref())
.unwrap_or(&vec![])
.to_owned(),
format: base_account.format.unwrap_or_default(),
mailboxes: base_account.mailboxes.clone(),
default: base_account.default.unwrap_or_default(),
email: base_account.email.to_owned(),

View file

@ -1,6 +1,8 @@
use serde::Deserialize;
use std::{collections::HashMap, path::PathBuf};
use crate::config::Format;
pub trait ToDeserializedBaseAccountConfig {
fn to_base(&self) -> DeserializedBaseAccountConfig;
}
@ -47,6 +49,9 @@ macro_rules! make_account_config {
pub notify_query: Option<String>,
/// Overrides the watch commands for this account.
pub watch_cmds: Option<Vec<String>>,
/// Represents the text/plain format as defined in the
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
pub format: Option<Format>,
/// Makes this account the default one.
pub default: Option<bool>,
@ -89,6 +94,7 @@ macro_rules! make_account_config {
notify_cmd: self.notify_cmd.clone(),
notify_query: self.notify_query.clone(),
watch_cmds: self.watch_cmds.clone(),
format: self.format.clone(),
default: self.default.clone(),
email: self.email.clone(),

23
src/config/format.rs Normal file
View file

@ -0,0 +1,23 @@
use serde::Deserialize;
/// Represents the text/plain format as defined in the [RFC2646]. The
/// format is then used by the table system to adjust the way it is
/// rendered.
///
/// [RFC2646]: https://www.ietf.org/rfc/rfc2646.txt
#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
#[serde(tag = "type", content = "width", rename_all = "lowercase")]
pub enum Format {
// Forces the content width with a fixed amount of pixels.
Fixed(usize),
// Makes the content fit the terminal.
Auto,
// Does not restrict the content.
Flowed,
}
impl Default for Format {
fn default() -> Self {
Self::Auto
}
}

View file

@ -1,11 +0,0 @@
//! This barrel module provides everything related to the user configuration.
pub mod config_args;
pub mod deserialized_config;
pub use deserialized_config::*;
pub mod account_args;
pub mod account_config;
pub use account_config::*;
pub mod deserialized_account_config;
pub use deserialized_account_config::*;

View file

@ -34,37 +34,36 @@ pub mod msg {
}
pub mod backends {
pub use backend::*;
pub mod backend;
pub use backend::*;
pub use id_mapper::*;
pub mod id_mapper;
pub use id_mapper::*;
pub use self::imap::*;
pub mod imap {
pub mod imap_arg;
pub use imap_backend::*;
pub mod imap_backend;
pub use imap_backend::*;
pub mod imap_handler;
pub use imap_mbox::*;
pub mod imap_mbox;
pub use imap_mbox::*;
pub use imap_mbox_attr::*;
pub mod imap_mbox_attr;
pub use imap_mbox_attr::*;
pub use imap_envelope::*;
pub mod imap_envelope;
pub use imap_envelope::*;
pub use imap_flag::*;
pub mod imap_flag;
pub use imap_flag::*;
pub mod msg_sort_criterion;
}
pub use self::imap::*;
pub use self::maildir::*;
pub mod maildir {
pub mod maildir_backend;
pub use maildir_backend::*;
@ -78,6 +77,7 @@ pub mod backends {
pub mod maildir_flag;
pub use maildir_flag::*;
}
pub use self::maildir::*;
#[cfg(feature = "notmuch")]
pub use self::notmuch::*;
@ -99,7 +99,21 @@ pub mod smtp {
pub use smtp_service::*;
}
pub mod config {
pub mod config_args;
pub mod deserialized_config;
pub use deserialized_config::*;
pub mod account_args;
pub mod account_config;
pub use account_config::*;
pub mod deserialized_account_config;
pub use deserialized_account_config::*;
pub mod format;
pub use format::*;
}
pub mod compl;
pub mod config;
pub mod output;
pub mod ui;

View file

@ -144,7 +144,7 @@ fn main() -> Result<()> {
// Check mailbox commands.
match mbox_arg::matches(&m)? {
Some(mbox_arg::Cmd::List(max_width)) => {
return mbox_handler::list(max_width, &mut printer, backend);
return mbox_handler::list(max_width, &account_config, &mut printer, backend);
}
_ => (),
}

View file

@ -7,19 +7,27 @@ use log::{info, trace};
use crate::{
backends::Backend,
config::AccountConfig,
output::{PrintTableOpts, PrinterService},
};
/// Lists all mailboxes.
pub fn list<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
max_width: Option<usize>,
config: &AccountConfig,
printer: &mut P,
backend: Box<&'a mut B>,
) -> Result<()> {
info!("entering list mailbox handler");
let mboxes = backend.get_mboxes()?;
trace!("mailboxes: {:?}", mboxes);
printer.print_table(mboxes, PrintTableOpts { max_width })
printer.print_table(
mboxes,
PrintTableOpts {
format: &config.format,
max_width,
},
)
}
#[cfg(test)]
@ -159,11 +167,12 @@ mod tests {
}
}
let config = AccountConfig::default();
let mut printer = PrinterServiceTest::default();
let mut backend = TestBackend {};
let backend = Box::new(&mut backend);
assert!(list(None, &mut printer, backend).is_ok());
assert!(list(None, &config, &mut printer, backend).is_ok());
assert_eq!(
concat![
"\n",

View file

@ -110,7 +110,13 @@ pub fn list<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
debug!("page size: {}", page_size);
let msgs = imap.get_envelopes(mbox, page_size, page)?;
trace!("envelopes: {:?}", msgs);
printer.print_table(msgs, PrintTableOpts { max_width })
printer.print_table(
msgs,
PrintTableOpts {
format: &config.format,
max_width,
},
)
}
/// Parses and edits a message from a [mailto] URL string.
@ -275,7 +281,13 @@ pub fn search<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
debug!("page size: {}", page_size);
let msgs = backend.search_envelopes(mbox, &query, "", page_size, page)?;
trace!("messages: {:#?}", msgs);
printer.print_table(msgs, PrintTableOpts { max_width })
printer.print_table(
msgs,
PrintTableOpts {
format: &config.format,
max_width,
},
)
}
/// Paginates messages from the selected mailbox matching the specified query, sorted by the given criteria.
@ -294,7 +306,13 @@ pub fn sort<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
debug!("page size: {}", page_size);
let msgs = backend.search_envelopes(mbox, &query, &sort, page_size, page)?;
trace!("envelopes: {:#?}", msgs);
printer.print_table(msgs, PrintTableOpts { max_width })
printer.print_table(
msgs,
PrintTableOpts {
format: &config.format,
max_width,
},
)
}
/// Send a raw message.

View file

@ -2,6 +2,8 @@ use anyhow::Result;
use std::io;
use termcolor::{self, StandardStream};
use crate::config::Format;
pub trait WriteColor: io::Write + termcolor::WriteColor {}
impl WriteColor for StandardStream {}
@ -10,6 +12,7 @@ pub trait PrintTable {
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()>;
}
pub struct PrintTableOpts {
pub struct PrintTableOpts<'a> {
pub format: &'a Format,
pub max_width: Option<usize>,
}

View file

@ -10,7 +10,10 @@ use termcolor::{Color, ColorSpec};
use terminal_size;
use unicode_width::UnicodeWidthStr;
use crate::output::{Print, PrintTableOpts, WriteColor};
use crate::{
config::Format,
output::{Print, PrintTableOpts, WriteColor},
};
/// Defines the default terminal size.
/// This is used when the size cannot be determined by the `terminal_size` crate.
@ -164,10 +167,15 @@ where
/// Writes the table to the writter.
fn print(writter: &mut dyn WriteColor, items: &[Self], opts: PrintTableOpts) -> Result<()> {
let max_width = opts
.max_width
.or_else(|| terminal_size::terminal_size().map(|(w, _)| w.0 as usize))
.unwrap_or(DEFAULT_TERM_WIDTH);
let is_format_flowed = matches!(opts.format, Format::Flowed);
let max_width = match opts.format {
Format::Fixed(width) => opts.max_width.unwrap_or(*width),
Format::Flowed => 0,
Format::Auto => opts
.max_width
.or_else(|| terminal_size::terminal_size().map(|(w, _)| w.0 as usize))
.unwrap_or(DEFAULT_TERM_WIDTH),
};
let mut table = vec![Self::head()];
let mut cell_widths: Vec<usize> =
table[0].0.iter().map(|cell| cell.unicode_width()).collect();
@ -195,7 +203,7 @@ where
glue.print(writter)?;
let table_is_overflowing = table_width > max_width;
if table_is_overflowing && cell.is_shrinkable() {
if table_is_overflowing && !is_format_flowed && cell.is_shrinkable() {
trace!("table is overflowing and cell is shrinkable");
let shrink_width = table_width - max_width;
@ -329,7 +337,7 @@ mod tests {
macro_rules! write_items {
($writter:expr, $($item:expr),*) => {
Table::print($writter, &[$($item,)*], PrintTableOpts { max_width: Some(20) }).unwrap();
Table::print($writter, &[$($item,)*], PrintTableOpts { format: &Format::Auto, max_width: Some(20) }).unwrap();
};
}