Add `view` subcommand

Add subcommand to view standalone e-mail files in meli's pager without
instantiating any accounts.
master
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.
# 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]]
name = "arc-swap"
version = "0.4.7"
@ -36,17 +27,6 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "autocfg"
version = "1.0.0"
@ -145,13 +125,9 @@ version = "2.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
@ -1613,12 +1589,6 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.14"
@ -1861,12 +1831,6 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55d1e41d56121e07f1e223db0a4def204e45c85425f6a16d462fd07c8d10d74c"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.2"

View File

@ -49,7 +49,7 @@ rmp-serde = "^0.14.0"
smallvec = { version = "1.1.0", features = ["serde", ] }
bitflags = "1.0"
pcre2 = { version = "0.2.3", optional = true }
structopt = { version = "0.3.14" }
structopt = { version = "0.3.14", default-features = false }
[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.
.It Cm print-loaded-themes
Print all loaded themes in TOML syntax.
.It Cm view
View mail from input file.
.El
.Sh DESCRIPTION
.Nm

View File

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

View File

@ -499,6 +499,30 @@ impl Settings {
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)]

View File

@ -207,7 +207,11 @@ impl Drop for 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
* stdin, see get_events() for details
@ -216,7 +220,11 @@ impl State {
let input_thread_pipe = nix::unistd::pipe()
.map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)?;
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();
for (_, p) in settings.plugins.clone() {
if crate::plugins::PluginKind::Backend == p.kind() {