cli.rs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. use std::{
  2. env, fs,
  3. io::Write,
  4. marker::PhantomData,
  5. path::{Path, PathBuf},
  6. str,
  7. time::Duration,
  8. };
  9. use indicatif::{ProgressBar, ProgressStyle};
  10. use serde::{de::DeserializeOwned, Serialize};
  11. use simplelog::ConfigBuilder;
  12. use crate::{Error, Result};
  13. #[derive(Clone, Default)]
  14. pub struct Config<T> {
  15. config: PhantomData<T>,
  16. }
  17. impl<T: Serialize + DeserializeOwned> Config<T> {
  18. pub fn load(path: PathBuf) -> Result<T> {
  19. if Path::new(&path).exists() {
  20. let toml = fs::read(&path)?;
  21. let str_buff = str::from_utf8(&toml)?;
  22. let config: T = toml::from_str(str_buff)?;
  23. Ok(config)
  24. } else {
  25. let path = path.to_str();
  26. if path.is_some() {
  27. println!("Could not find/parse configuration file in: {}", path.unwrap());
  28. } else {
  29. println!("Could not find/parse configuration file");
  30. }
  31. println!("Please follow the instructions in the README");
  32. Err(Error::ConfigNotFound)
  33. }
  34. }
  35. }
  36. pub fn spawn_config(path: &Path, contents: &[u8]) -> Result<()> {
  37. if !path.exists() {
  38. if let Some(parent) = path.parent() {
  39. fs::create_dir_all(parent)?;
  40. }
  41. let mut file = fs::File::create(path)?;
  42. file.write_all(contents)?;
  43. println!("Config file created in '{:?}'. Please review it and try again.", path);
  44. std::process::exit(2);
  45. }
  46. Ok(())
  47. }
  48. pub fn get_log_level(verbosity_level: u64) -> simplelog::LevelFilter {
  49. match verbosity_level {
  50. 0 => simplelog::LevelFilter::Info,
  51. 1 => simplelog::LevelFilter::Debug,
  52. _ => simplelog::LevelFilter::Trace,
  53. }
  54. }
  55. pub fn get_log_config() -> simplelog::Config {
  56. match env::var("LOG_TARGETS") {
  57. Ok(x) => {
  58. let targets: Vec<String> = x.split(',').map(|x| x.to_string()).collect();
  59. let mut cfgbuilder = ConfigBuilder::new();
  60. for i in targets {
  61. if i.starts_with('!') {
  62. cfgbuilder.add_filter_ignore(i.trim_start_matches('!').to_string());
  63. } else {
  64. cfgbuilder.add_filter_allow(i);
  65. }
  66. }
  67. cfgbuilder.build()
  68. }
  69. Err(_) => simplelog::Config::default(),
  70. }
  71. }
  72. pub const ANSI_LOGO: &str = include_str!("../../contrib/darkfi.ansi");
  73. #[macro_export]
  74. macro_rules! cli_desc {
  75. () => {{
  76. let mut desc = env!("CARGO_PKG_DESCRIPTION").to_string();
  77. desc.push_str("\n");
  78. desc.push_str(darkfi::util::cli::ANSI_LOGO);
  79. Box::leak(desc.into_boxed_str()) as &'static str
  80. }};
  81. }
  82. /// This macro is used for a standard way of daemonizing darkfi binaries
  83. /// with TOML config file configuration, and argument parsing. It also
  84. /// spawns a multithreaded async executor and passes it into the given
  85. /// function.
  86. ///
  87. /// The Cargo.toml dependencies needed for this are:
  88. /// ```text
  89. /// async-channel = "1.6.1"
  90. /// async-executor = "1.4.1"
  91. /// async-std = "1.11.0"
  92. /// darkfi = { path = "../../", features = ["util"] }
  93. /// easy-parallel = "3.2.0"
  94. /// futures-lite = "1.12.0"
  95. /// simplelog = "0.12.0-alpha1"
  96. ///
  97. /// # Argument parsing
  98. /// serde = "1.0.136"
  99. /// serde_derive = "1.0.136"
  100. /// structopt = "0.3.26"
  101. /// structopt-toml = "0.5.0"
  102. /// ```
  103. ///
  104. /// Example usage:
  105. /// ```text
  106. /// use async_std::sync::Arc;
  107. /// use futures_lite::future;
  108. /// use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
  109. ///
  110. /// use darkfi::{
  111. /// async_daemonize, cli_desc,
  112. /// util::{
  113. /// cli::{get_log_config, get_log_level, spawn_config},
  114. /// path::get_config_path,
  115. /// },
  116. /// Result,
  117. /// };
  118. ///
  119. /// const CONFIG_FILE: &str = "daemond_config.toml";
  120. /// const CONFIG_FILE_CONTENTS: &str = include_str!("../daemond_config.toml");
  121. ///
  122. /// #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  123. /// #[serde(default)]
  124. /// #[structopt(name = "daemond", about = cli_desc!())]
  125. /// struct Args {
  126. /// #[structopt(short, long)]
  127. /// /// Configuration file to use
  128. /// config: Option<String>,
  129. ///
  130. /// #[structopt(short, parse(from_occurrences))]
  131. /// /// Increase verbosity (-vvv supported)
  132. /// verbose: u8,
  133. /// }
  134. ///
  135. /// async_daemonize!(realmain);
  136. /// async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
  137. /// println!("Hello, world!");
  138. /// Ok(())
  139. /// }
  140. /// ```
  141. #[macro_export]
  142. macro_rules! async_daemonize {
  143. ($realmain:ident) => {
  144. fn main() -> Result<()> {
  145. let args = Args::from_args_with_toml("").unwrap();
  146. let cfg_path = get_config_path(args.config, CONFIG_FILE)?;
  147. spawn_config(&cfg_path, CONFIG_FILE_CONTENTS.as_bytes())?;
  148. let args = Args::from_args_with_toml(&std::fs::read_to_string(cfg_path)?).unwrap();
  149. let log_level = get_log_level(args.verbose.into());
  150. let log_config = get_log_config();
  151. let env_log_file_path = match std::env::var("DARKFI_LOG") {
  152. Ok(p) => std::fs::File::create(p).unwrap(),
  153. Err(_) => std::fs::File::create("/tmp/darkfi.log").unwrap(),
  154. };
  155. simplelog::CombinedLogger::init(vec![
  156. simplelog::TermLogger::new(
  157. log_level,
  158. log_config.clone(),
  159. simplelog::TerminalMode::Mixed,
  160. simplelog::ColorChoice::Auto,
  161. ),
  162. simplelog::WriteLogger::new(log_level, log_config, env_log_file_path),
  163. ])?;
  164. // https://docs.rs/smol/latest/smol/struct.Executor.html#examples
  165. let ex = Arc::new(async_executor::Executor::new());
  166. let (signal, shutdown) = async_channel::unbounded::<()>();
  167. let (_, result) = easy_parallel::Parallel::new()
  168. // Run four executor threads
  169. .each(0..4, |_| future::block_on(ex.run(shutdown.recv())))
  170. // Run the main future on the current thread.
  171. .finish(|| {
  172. future::block_on(async {
  173. $realmain(args, ex.clone()).await?;
  174. drop(signal);
  175. Ok::<(), darkfi::Error>(())
  176. })
  177. });
  178. result
  179. }
  180. };
  181. }
  182. pub fn progress_bar(message: &str) -> ProgressBar {
  183. let progress_bar = ProgressBar::new(42);
  184. progress_bar.set_style(
  185. ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}").unwrap(),
  186. );
  187. progress_bar.enable_steady_tick(Duration::from_millis(100));
  188. progress_bar.set_message(message.to_string());
  189. progress_bar
  190. }