/*
* meli - bin.rs
*
* Copyright 2017-2018 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see .
*/
//!
//! This crate contains the frontend stuff of the application. The application entry way on
//! `src/bin.rs` creates an event loop and passes input to a thread.
//!
//! The mail handling stuff is done in the `melib` crate which includes all backend needs. The
//! split is done to theoretically be able to create different frontends with the same innards.
//!
use std::alloc::System;
use std::collections::VecDeque;
use std::path::PathBuf;
#[macro_use]
extern crate serde_derive;
extern crate linkify;
extern crate uuid;
extern crate bitflags;
extern crate serde_json;
#[macro_use]
extern crate smallvec;
extern crate termion;
#[global_allocator]
static GLOBAL: System = System;
#[macro_use]
extern crate melib;
use melib::*;
#[macro_use]
pub mod types;
use crate::types::*;
#[macro_use]
pub mod terminal;
use crate::terminal::*;
#[macro_use]
pub mod command;
use crate::command::*;
pub mod state;
use crate::state::*;
pub mod components;
use crate::components::*;
#[macro_use]
pub mod conf;
use crate::conf::*;
#[cfg(feature = "sqlite3")]
pub mod sqlite3;
pub mod jobs;
pub mod mailcap;
use std::os::raw::c_int;
fn notify(
signals: &[c_int],
sender: crossbeam::channel::Sender,
) -> std::result::Result, std::io::Error> {
use std::time::Duration;
let (alarm_pipe_r, alarm_pipe_w) = nix::unistd::pipe().map_err(|err| {
std::io::Error::from_raw_os_error(err.as_errno().map(|n| n as i32).unwrap_or(0))
})?;
let alarm_handler = move |info: &nix::libc::siginfo_t| {
let value = unsafe { info.si_value().sival_ptr as u8 };
let _ = nix::unistd::write(alarm_pipe_w, &[value]);
};
unsafe {
signal_hook_registry::register_sigaction(signal_hook::SIGALRM, alarm_handler)?;
}
let (s, r) = crossbeam::channel::bounded(100);
let signals = signal_hook::iterator::Signals::new(signals)?;
let _ = nix::fcntl::fcntl(
alarm_pipe_r,
nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::OFlag::O_NONBLOCK),
);
std::thread::spawn(move || {
let mut ctr = 0;
loop {
ctr %= 3;
if ctr == 0 {
let _ = sender
.send_timeout(ThreadEvent::Pulse, Duration::from_millis(500))
.ok();
}
for signal in signals.pending() {
let _ = s.send_timeout(signal, Duration::from_millis(500)).ok();
}
std::thread::sleep(std::time::Duration::from_millis(100));
ctr += 1;
}
});
Ok(r)
}
fn parse_manpage(src: &str) -> Result {
match src {
"" | "meli" | "main" => Ok(ManPages::Main),
"meli.conf" | "conf" | "config" | "configuration" => Ok(ManPages::Conf),
"meli-themes" | "themes" | "theming" | "theme" => Ok(ManPages::Themes),
_ => Err(MeliError::new(format!(
"Invalid documentation page: {}",
src
))),
}
}
use structopt::StructOpt;
#[derive(Copy, Clone, Debug)]
/// Choose manpage
enum ManPages {
/// meli(1)
Main = 0,
/// meli.conf(5)
Conf = 1,
/// meli-themes(5)
Themes = 2,
}
#[derive(Debug, StructOpt)]
#[structopt(name = "meli", about = "terminal mail client", version_short = "v")]
struct Opt {
/// use specified configuration file
#[structopt(short, long, parse(from_os_str))]
config: Option,
#[structopt(subcommand)]
subcommand: Option,
}
#[derive(Debug, StructOpt)]
enum SubCommand {
/// print default theme in full to stdout and exit.
PrintDefaultTheme,
/// print loaded themes in full to stdout and exit.
PrintLoadedThemes,
/// create a sample configuration file with available configuration options. If PATH is not specified, meli will try to create it in $XDG_CONFIG_HOME/meli/config.toml
#[structopt(display_order = 1)]
CreateConfig {
#[structopt(value_name = "NEW_CONFIG_PATH", parse(from_os_str))]
path: Option,
},
/// test a configuration file for syntax issues or missing options.
#[structopt(display_order = 2)]
TestConfig {
#[structopt(value_name = "CONFIG_PATH", parse(from_os_str))]
path: Option,
},
#[structopt(visible_alias="docs", aliases=&["docs", "manpage", "manpages"])]
#[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)]
struct ManOpt {
#[structopt(default_value = "meli", possible_values=&["meli", "conf", "themes"], value_name="PAGE", parse(try_from_str = parse_manpage))]
page: ManPages,
/// If true, output text in stdout instead of spawning $PAGER.
#[structopt(long = "no-raw", alias = "no-raw", value_name = "bool")]
no_raw: Option