2021-02-23 04:30:20 +00:00
|
|
|
use std::path::{Path, PathBuf};
|
2021-02-26 06:26:37 +00:00
|
|
|
use std::thread;
|
2021-02-12 13:44:08 +00:00
|
|
|
|
2021-02-12 08:12:43 +00:00
|
|
|
use anyhow::Result;
|
2021-02-22 07:37:19 +00:00
|
|
|
use fern::colors::{Color, ColoredLevelConfig};
|
2021-02-24 10:52:32 +00:00
|
|
|
use futures::future::TryFutureExt;
|
2021-02-16 10:45:41 +00:00
|
|
|
use panorama::{config::spawn_config_watcher_system, mail, report_err, ui};
|
2021-02-12 13:47:36 +00:00
|
|
|
use structopt::StructOpt;
|
2021-02-26 06:26:37 +00:00
|
|
|
use tokio::{
|
|
|
|
runtime::{Builder as RuntimeBuilder, Runtime},
|
|
|
|
sync::mpsc,
|
|
|
|
task::LocalSet,
|
|
|
|
};
|
2021-02-14 12:11:17 +00:00
|
|
|
use xdg::BaseDirectories;
|
2021-02-12 08:12:43 +00:00
|
|
|
|
2021-02-12 13:47:36 +00:00
|
|
|
#[derive(Debug, StructOpt)]
|
2021-02-12 13:52:46 +00:00
|
|
|
#[structopt(author, about)]
|
2021-02-12 13:47:36 +00:00
|
|
|
struct Opt {
|
|
|
|
/// The path to the log file. By default, does not log.
|
|
|
|
#[structopt(long = "log-file")]
|
|
|
|
log_file: Option<PathBuf>,
|
|
|
|
|
2021-02-23 04:30:20 +00:00
|
|
|
/// Run this application headlessly
|
|
|
|
#[structopt(long = "headless")]
|
|
|
|
headless: bool,
|
2021-02-24 10:01:18 +00:00
|
|
|
|
|
|
|
/// Don't watch the config file for changes. (NYI)
|
|
|
|
// TODO: implement this or decide if it's useless
|
|
|
|
#[structopt(long = "no-watch-config")]
|
|
|
|
_no_watch_config: bool,
|
2021-02-23 04:30:20 +00:00
|
|
|
}
|
2021-02-23 04:01:39 +00:00
|
|
|
|
2021-02-23 04:30:20 +00:00
|
|
|
fn main() -> Result<()> {
|
2021-02-14 12:11:17 +00:00
|
|
|
// parse command line arguments into options struct
|
2021-02-22 07:37:19 +00:00
|
|
|
let opt = Opt::from_args();
|
2021-02-23 04:30:20 +00:00
|
|
|
setup_logger(opt.log_file.as_ref())?;
|
2021-02-22 07:37:19 +00:00
|
|
|
|
2021-02-23 04:30:20 +00:00
|
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
rt.block_on(run(opt)).unwrap();
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-02-12 08:12:43 +00:00
|
|
|
|
2021-02-23 04:30:20 +00:00
|
|
|
// #[tokio::main(flavor = "multi_thread")]
|
|
|
|
async fn run(opt: Opt) -> Result<()> {
|
2021-02-15 10:36:06 +00:00
|
|
|
let _xdg = BaseDirectories::new()?;
|
2021-02-15 11:07:48 +00:00
|
|
|
let (_config_thread, config_update) = spawn_config_watcher_system()?;
|
2021-02-14 12:11:17 +00:00
|
|
|
|
|
|
|
// used to notify the runtime that the process should exit
|
2021-02-14 13:20:35 +00:00
|
|
|
let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1);
|
2021-02-14 12:11:17 +00:00
|
|
|
|
|
|
|
// used to send commands to the mail service
|
2021-02-15 10:36:06 +00:00
|
|
|
let (_mail_tx, mail_rx) = mpsc::unbounded_channel();
|
2021-02-12 08:12:43 +00:00
|
|
|
|
2021-02-16 10:45:41 +00:00
|
|
|
tokio::spawn(async move {
|
|
|
|
let config_update = config_update.clone();
|
|
|
|
mail::run_mail(config_update, mail_rx)
|
|
|
|
.unwrap_or_else(report_err)
|
|
|
|
.await;
|
|
|
|
});
|
2021-02-14 23:49:54 +00:00
|
|
|
|
2021-02-23 04:30:20 +00:00
|
|
|
if !opt.headless {
|
2021-02-26 06:26:37 +00:00
|
|
|
run_ui(exit_tx);
|
2021-02-23 04:30:20 +00:00
|
|
|
}
|
2021-02-12 08:12:43 +00:00
|
|
|
|
2021-02-14 13:20:35 +00:00
|
|
|
exit_rx.recv().await;
|
|
|
|
|
|
|
|
// TODO: graceful shutdown
|
|
|
|
// yada yada create a background process and pass off the connections so they can be safely
|
|
|
|
// shutdown
|
|
|
|
std::process::exit(0);
|
|
|
|
// Ok(())
|
2021-02-12 08:12:43 +00:00
|
|
|
}
|
2021-02-23 04:30:20 +00:00
|
|
|
|
2021-02-26 06:26:37 +00:00
|
|
|
// Spawns the entire UI in a different thread, since it must be thread-local
|
|
|
|
fn run_ui(exit_tx: mpsc::Sender<()>) {
|
|
|
|
let stdout = std::io::stdout();
|
|
|
|
|
|
|
|
let rt = RuntimeBuilder::new_current_thread()
|
|
|
|
.enable_all()
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
thread::spawn(move || {
|
|
|
|
let localset = LocalSet::new();
|
|
|
|
|
|
|
|
localset.spawn_local(async {
|
|
|
|
ui::run_ui(stdout, exit_tx).unwrap_or_else(report_err).await;
|
|
|
|
});
|
|
|
|
|
|
|
|
rt.block_on(localset);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-23 04:30:20 +00:00
|
|
|
fn setup_logger(log_file: Option<impl AsRef<Path>>) -> Result<()> {
|
|
|
|
let now = chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]");
|
|
|
|
let colors = ColoredLevelConfig::new()
|
|
|
|
.info(Color::Blue)
|
|
|
|
.debug(Color::BrightBlack)
|
|
|
|
.warn(Color::Yellow)
|
|
|
|
.error(Color::Red);
|
|
|
|
let mut logger = fern::Dispatch::new()
|
|
|
|
.format(move |out, message, record| {
|
|
|
|
out.finish(format_args!(
|
|
|
|
"{}[{}][{}] {}",
|
|
|
|
now,
|
|
|
|
record.target(),
|
|
|
|
colors.color(record.level()),
|
|
|
|
message
|
|
|
|
))
|
|
|
|
})
|
|
|
|
.level(log::LevelFilter::Debug);
|
|
|
|
if let Some(log_file) = log_file {
|
|
|
|
logger = logger.chain(fern::log_file(log_file)?);
|
|
|
|
}
|
|
|
|
logger.apply()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|