You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.rs 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. //#![allow(unused)]
  2. #![allow(incomplete_features)]
  3. #![feature(impl_trait_in_bindings)]
  4. #![feature(async_closure)]
  5. use std::collections::*;
  6. use std::path::PathBuf;
  7. use argh::FromArgs;
  8. use tokio::runtime;
  9. use tokio::sync::mpsc;
  10. mod client;
  11. mod config;
  12. mod spool;
  13. use crate::config::*;
  14. #[derive(FromArgs, PartialEq, Debug)]
  15. #[argh(description = "mtxspooler")]
  16. struct Opts {
  17. #[argh(
  18. option,
  19. short = 'c',
  20. description = "read this config file by itself, parsed before -C"
  21. )]
  22. conf: Option<PathBuf>,
  23. #[argh(
  24. option,
  25. short = 'C',
  26. description = "read all config files in this directory"
  27. )]
  28. conf_dir: Option<PathBuf>,
  29. #[argh(
  30. option,
  31. short = 'r',
  32. description = "delete this file to trigger a config reload [NYI]"
  33. )]
  34. reload_trigger: Option<PathBuf>,
  35. }
  36. fn main() {
  37. let opts: Opts = argh::from_env();
  38. if opts.reload_trigger.is_some() {
  39. eprintln!("[init] warning: reload trigger file specified, but this option is not supported yet, ignoring...");
  40. }
  41. let mut rt = make_runtime();
  42. rt.block_on(main_inner(opts));
  43. }
  44. async fn main_inner(opts: Opts) {
  45. // Figure out which files we want to configure.
  46. let mut confs = Vec::new();
  47. if let Some(main) = opts.conf {
  48. confs.push(main.clone());
  49. }
  50. if let Some(dir) = opts.conf_dir {
  51. match find_configs(&dir).await {
  52. Ok(paths) => confs.extend(paths),
  53. Err(e) => {
  54. eprintln!("[init] error reading configuration: {:?}", e);
  55. return;
  56. }
  57. }
  58. }
  59. // Sanity check.
  60. if confs.len() == 0 {
  61. println!("[init] no configuration declared, exiting...");
  62. return;
  63. }
  64. // Process configuration.
  65. // TODO Remove all these cases of `block_on` except for a final toplevel task.
  66. let config = match parse_configs(&confs).await {
  67. Ok(c) => c,
  68. Err(e) => {
  69. eprintln!("[init] error parsing configuration: {:?}", e);
  70. return; // maybe
  71. }
  72. };
  73. let mut clients = HashMap::new();
  74. let mut sender_futs = Vec::new();
  75. // Init each account and put outgoing channels into a table for later reference.
  76. for cc in config.accounts.iter() {
  77. match client::create_and_auth_client(cc.1.clone()).await {
  78. Ok(c) => {
  79. let (send, recv) = mpsc::channel(2); // FIXME configurable
  80. clients.insert(cc.0.clone(), send);
  81. sender_futs.push(client::submit_messages(c, recv));
  82. }
  83. Err(e) => {
  84. eprintln!("[init] error: client setup failed: {:?}", e);
  85. return; // maybe
  86. }
  87. }
  88. }
  89. // This is where the real stuff actually happens.
  90. match spool::start_spoolers(config, clients).await {
  91. Ok(_) => {}
  92. Err(e) => println!("fatal error: {:?}", e),
  93. }
  94. // just wait I guess?
  95. futures::future::join_all(sender_futs.into_iter()).await;
  96. }
  97. fn make_runtime() -> runtime::Runtime {
  98. runtime::Builder::new_current_thread()
  99. .thread_name("mtxspooler-worker")
  100. .enable_all()
  101. .build()
  102. .expect("rt: init")
  103. }