//#![allow(unused)] #![allow(incomplete_features)] #![feature(impl_trait_in_bindings)] #![feature(async_closure)] use std::collections::*; use std::path::PathBuf; use argh::FromArgs; use tokio::runtime; use tokio::sync::mpsc; mod client; mod config; mod spool; use crate::config::*; #[derive(FromArgs, PartialEq, Debug)] #[argh(description = "mtxspooler")] struct Opts { #[argh( option, short = 'c', description = "read this config file by itself, parsed before -C" )] conf: Option, #[argh( option, short = 'C', description = "read all config files in this directory" )] conf_dir: Option, #[argh( option, short = 'r', description = "delete this file to trigger a config reload [NYI]" )] reload_trigger: Option, } fn main() { let opts: Opts = argh::from_env(); if opts.reload_trigger.is_some() { eprintln!("[init] warning: reload trigger file specified, but this option is not supported yet, ignoring..."); } let mut rt = make_runtime(); rt.block_on(main_inner(opts)); } async fn main_inner(opts: Opts) { // Figure out which files we want to configure. let mut confs = Vec::new(); if let Some(main) = opts.conf { confs.push(main.clone()); } if let Some(dir) = opts.conf_dir { match find_configs(&dir).await { Ok(paths) => confs.extend(paths), Err(e) => { eprintln!("[init] error reading configuration: {:?}", e); return; } } } // Sanity check. if confs.len() == 0 { println!("[init] no configuration declared, exiting..."); return; } // Process configuration. // TODO Remove all these cases of `block_on` except for a final toplevel task. let config = match parse_configs(&confs).await { Ok(c) => c, Err(e) => { eprintln!("[init] error parsing configuration: {:?}", e); return; // maybe } }; let mut clients = HashMap::new(); let mut sender_futs = Vec::new(); // Init each account and put outgoing channels into a table for later reference. for cc in config.accounts.iter() { match client::create_and_auth_client(cc.1.clone()).await { Ok(c) => { let (send, recv) = mpsc::channel(2); // FIXME configurable clients.insert(cc.0.clone(), send); sender_futs.push(client::submit_messages(c, recv)); } Err(e) => { eprintln!("[init] error: client setup failed: {:?}", e); return; // maybe } } } // This is where the real stuff actually happens. match spool::start_spoolers(config, clients).await { Ok(_) => {} Err(e) => println!("fatal error: {:?}", e), } // just wait I guess? futures::future::join_all(sender_futs.into_iter()).await; } fn make_runtime() -> runtime::Runtime { runtime::Builder::new_current_thread() .thread_name("mtxspooler-worker") .enable_all() .build() .expect("rt: init") }