Add `view` subcommand

Add subcommand to view standalone e-mail files in meli's pager without
instantiating any accounts.
async
Manos Pitsidianakis 2020-06-10 18:06:28 +03:00
parent 7dc8a87a62
commit e97cf98b3b
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
6 changed files with 87 additions and 56 deletions

36
Cargo.lock generated
View File

@ -1,14 +1,5 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.8",
]
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "0.4.7" version = "0.4.7"
@ -36,17 +27,6 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi 0.3.8",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.0" version = "1.0.0"
@ -145,13 +125,9 @@ version = "2.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
dependencies = [ dependencies = [
"ansi_term",
"atty",
"bitflags", "bitflags",
"strsim",
"textwrap", "textwrap",
"unicode-width", "unicode-width",
"vec_map",
] ]
[[package]] [[package]]
@ -1613,12 +1589,6 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "structopt" name = "structopt"
version = "0.3.14" version = "0.3.14"
@ -1861,12 +1831,6 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55d1e41d56121e07f1e223db0a4def204e45c85425f6a16d462fd07c8d10d74c" checksum = "55d1e41d56121e07f1e223db0a4def204e45c85425f6a16d462fd07c8d10d74c"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.2" version = "0.9.2"

View File

@ -49,7 +49,7 @@ rmp-serde = "^0.14.0"
smallvec = { version = "1.1.0", features = ["serde", ] } smallvec = { version = "1.1.0", features = ["serde", ] }
bitflags = "1.0" bitflags = "1.0"
pcre2 = { version = "0.2.3", optional = true } pcre2 = { version = "0.2.3", optional = true }
structopt = { version = "0.3.14" } structopt = { version = "0.3.14", default-features = false }
[profile.release] [profile.release]

2
meli.1
View File

@ -48,6 +48,8 @@ Print documentation page and exit (Piping to a pager is recommended.)
Print default theme keys and values in TOML syntax, to be used as a blueprint. Print default theme keys and values in TOML syntax, to be used as a blueprint.
.It Cm print-loaded-themes .It Cm print-loaded-themes
Print all loaded themes in TOML syntax. Print all loaded themes in TOML syntax.
.It Cm view
View mail from input file.
.El .El
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm

View File

@ -199,6 +199,12 @@ enum SubCommand {
#[structopt(display_order = 3)] #[structopt(display_order = 3)]
/// print documentation page and exit (Piping to a pager is recommended.). /// print documentation page and exit (Piping to a pager is recommended.).
Man(ManOpt), Man(ManOpt),
/// View mail from input file.
View {
#[structopt(value_name = "INPUT", parse(from_os_str))]
path: PathBuf,
},
} }
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
@ -272,6 +278,19 @@ fn run_app(opt: Opt) -> Result<()> {
print!("{}", conf::Themes::default().key_to_string("dark", false)); print!("{}", conf::Themes::default().key_to_string("dark", false));
return Ok(()); return Ok(());
} }
Some(SubCommand::View { ref path }) => {
if !path.exists() {
return Err(MeliError::new(format!(
"`{}` is not a valid path",
path.display()
)));
} else if !path.is_file() {
return Err(MeliError::new(format!(
"`{}` is a directory",
path.display()
)));
}
}
None => {} None => {}
} }
@ -289,26 +308,40 @@ fn run_app(opt: Opt) -> Result<()> {
let signal_recvr = notify(signals, sender.clone())?; let signal_recvr = notify(signals, sender.clone())?;
/* Create the application State. */ /* Create the application State. */
let mut state = State::new(sender, receiver.clone())?; let mut state;
let window = Box::new(Tabbed::new( if let Some(SubCommand::View { path }) = opt.subcommand {
vec![ let bytes = std::fs::read(&path)
Box::new(listing::Listing::new(&mut state.context)), .chain_err_summary(|| format!("Could not read from `{}`", path.display()))?;
Box::new(ContactList::new(&state.context)), let wrapper = EnvelopeWrapper::new(bytes)
Box::new(StatusPanel::new(crate::conf::value( .chain_err_summary(|| format!("Could not parse `{}`", path.display()))?;
&state.context, state = State::new(
"theme_default", Some(Settings::without_accounts().unwrap_or_default()),
))), sender,
], receiver.clone(),
&state.context, )?;
)); state.register_component(Box::new(EnvelopeView::new(wrapper, None, None, 0)));
} else {
state = State::new(None, sender, receiver.clone())?;
let window = Box::new(Tabbed::new(
vec![
Box::new(listing::Listing::new(&mut state.context)),
Box::new(ContactList::new(&state.context)),
Box::new(StatusPanel::new(crate::conf::value(
&state.context,
"theme_default",
))),
],
&state.context,
));
let status_bar = Box::new(StatusBar::new(window)); let status_bar = Box::new(StatusBar::new(window));
state.register_component(status_bar); state.register_component(status_bar);
let xdg_notifications = Box::new(components::notifications::XDGNotifications::new()); let xdg_notifications = Box::new(components::notifications::XDGNotifications::new());
state.register_component(xdg_notifications); state.register_component(xdg_notifications);
state.register_component(Box::new(components::notifications::NotificationFilter {})); state.register_component(Box::new(components::notifications::NotificationFilter {}));
}
/* Keep track of the input mode. See UIMode for details */ /* Keep track of the input mode. See UIMode for details */
'main: loop { 'main: loop {

View File

@ -499,6 +499,30 @@ impl Settings {
log: fs.log, log: fs.log,
}) })
} }
pub fn without_accounts() -> Result<Settings> {
let fs = FileSettings::new()?;
if let Some(ref log_path) = fs.log.log_file {
melib::change_log_dest(log_path.into());
}
if fs.log.maximum_level != melib::LoggingLevel::default() {
melib::change_log_level(fs.log.maximum_level);
}
Ok(Settings {
accounts: HashMap::new(),
pager: fs.pager,
listing: fs.listing,
notifications: fs.notifications,
shortcuts: fs.shortcuts,
tags: fs.tags,
composing: fs.composing,
pgp: fs.pgp,
terminal: fs.terminal,
plugins: fs.plugins,
log: fs.log,
})
}
} }
#[derive(Copy, Debug, Clone, Hash, PartialEq)] #[derive(Copy, Debug, Clone, Hash, PartialEq)]

View File

@ -207,7 +207,11 @@ impl Drop for State {
} }
impl State { impl State {
pub fn new(sender: Sender<ThreadEvent>, receiver: Receiver<ThreadEvent>) -> Result<Self> { pub fn new(
settings: Option<Settings>,
sender: Sender<ThreadEvent>,
receiver: Receiver<ThreadEvent>,
) -> Result<Self> {
/* /*
* Create async channel to block the input-thread if we need to fork and stop it from reading * Create async channel to block the input-thread if we need to fork and stop it from reading
* stdin, see get_events() for details * stdin, see get_events() for details
@ -216,7 +220,11 @@ impl State {
let input_thread_pipe = nix::unistd::pipe() let input_thread_pipe = nix::unistd::pipe()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)?; .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)?;
let mut backends = Backends::new(); let mut backends = Backends::new();
let settings = Settings::new()?; let settings = if let Some(settings) = settings {
settings
} else {
Settings::new()?
};
let mut plugin_manager = PluginManager::new(); let mut plugin_manager = PluginManager::new();
for (_, p) in settings.plugins.clone() { for (_, p) in settings.plugins.clone() {
if crate::plugins::PluginKind::Backend == p.kind() { if crate::plugins::PluginKind::Backend == p.kind() {