diff --git a/src/util/cli.rs b/src/util/cli.rs index 79b5de973..6009ba4f7 100644 --- a/src/util/cli.rs +++ b/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, +/// +/// #[structopt(short, parse(from_occurrences))] +/// /// Increase verbosity (-vvv supported) +/// verbose: u8, +/// } +/// +/// async_daemonize!(realmain); +/// async fn realmain(args: Args, ex: Arc>) -> 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 + } + }; +}