Просмотр исходного кода

util/cli: Add macro for standardized daemonization.

parazyd 4 лет назад
Родитель
Сommit
7fb58b3d2b
1 измененных файлов с 94 добавлено и 0 удалено
  1. 94 0
      src/util/cli.rs

+ 94 - 0
src/util/cli.rs

@@ -120,3 +120,97 @@ macro_rules! cli_desc {
         Box::leak(desc.into_boxed_str()) as &'static str
     }};
 }
+
+/// This macro is used for a standard way of daemonizing darkfi binaries
+/// with TOML config file configuration, and argument parsing. It also
+/// spawns a multithreaded async executor and passes it into the given
+/// function.
+///
+/// The Cargo.toml dependencies needed for this are:
+/// ```text
+/// async-channel = "1.6.1"
+/// async-executor = "1.4.1"
+/// async-std = "1.11.0"
+/// darkfi = { path = "../../", features = ["util"] }
+/// easy-parallel = "3.2.0"
+/// futures-lite = "1.12.0"
+/// simplelog = "0.12.0-alpha1"
+///
+/// # Argument parsing
+/// serde = "1.0.136"
+/// serde_derive = "1.0.136"
+/// structopt = "0.3.26"
+/// structopt-toml = "0.5.0"
+/// ```
+///
+/// Example usage:
+/// ```text
+/// use async_executor::Executor;
+/// use async_std::sync::Arc;
+/// use easy_parallel::Parallel;
+/// use futures_lite::future;
+/// use simplelog::{ColorChoice, TermLogger, TerminalMode};
+/// use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
+///
+/// use darkfi::{
+///     async_daemonize, cli_desc,
+///     util::{
+///         cli::{log_config, spawn_config},
+///         path::get_config_path,
+///     },
+///     Result,
+/// };
+///
+/// const CONFIG_FILE: &str = "daemond_config.toml";
+/// const CONFIG_FILE_CONTENTS: &str = include_str!("../daemond_config.toml");
+///
+/// #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
+/// #[serde(default)]
+/// #[structopt(name = "daemond", about = cli_desc!())]
+/// struct Args {
+///     #[structopt(short, long)]
+///     /// Configuration file to use
+///     config: Option<String>,
+///
+///     #[structopt(short, parse(from_occurrences))]
+///     /// Increase verbosity (-vvv supported)
+///     verbose: u8,
+/// }
+///
+/// async_daemonize!(realmain);
+/// async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
+///     println!("Hello, world!");
+///     Ok(())
+/// }
+/// ```
+#[macro_export]
+macro_rules! async_daemonize {
+    ($realmain:ident) => {
+        fn main() -> Result<()> {
+            let args = Args::from_args_with_toml("").unwrap();
+            let cfg_path = get_config_path(args.config, CONFIG_FILE)?;
+            spawn_config(&cfg_path, CONFIG_FILE_CONTENTS.as_bytes())?;
+            let args = Args::from_args_with_toml(&std::fs::read_to_string(cfg_path)?).unwrap();
+
+            let (lvl, conf) = log_config(args.verbose.into())?;
+            TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
+
+            // https://docs.rs/smol/latest/smol/struct.Executor.html#examples
+            let ex = Arc::new(Executor::new());
+            let (signal, shutdown) = async_channel::unbounded::<()>();
+            let (_, result) = Parallel::new()
+                // Run four executor threads
+                .each(0..4, |_| future::block_on(ex.run(shutdown.recv())))
+                // Run the main future on the current thread.
+                .finish(|| {
+                    future::block_on(async {
+                        $realmain(args, ex.clone()).await?;
+                        drop(signal);
+                        Ok::<(), darkfi::Error>(())
+                    })
+                });
+
+            result
+        }
+    };
+}