1use crate::cli_shared::cli::{CliOpts, HELP_MESSAGE};
5use crate::cli_shared::{
6 cli::{ConfigPath, check_for_unknown_keys},
7 logger,
8};
9use crate::utils::version::FOREST_VERSION_STRING;
10use anyhow::Context as _;
11use clap::Parser;
12use std::ffi::OsString;
13use std::time::Duration;
14use tracing::info;
15
16#[derive(Parser)]
18#[command(name = env!("CARGO_PKG_NAME"), bin_name = "forest", author = env!("CARGO_PKG_AUTHORS"), version = FOREST_VERSION_STRING.as_str(), about = env!("CARGO_PKG_DESCRIPTION")
19)]
20#[command(help_template(HELP_MESSAGE))]
21pub struct Cli {
22 #[clap(flatten)]
23 pub opts: CliOpts,
24}
25
26pub fn main<ArgT>(args: impl IntoIterator<Item = ArgT>) -> anyhow::Result<()>
27where
28 ArgT: Into<OsString> + Clone,
29{
30 let Cli { opts } = Cli::parse_from(args);
32
33 let (cfg, path) = opts.to_config().context("Error parsing config")?;
34
35 let background_tasks = logger::setup_logger(&opts);
39
40 if let Some(path) = &path {
41 match path {
42 ConfigPath::Env(path) => {
43 info!("FOREST_CONFIG_PATH loaded: {}", path.display())
44 }
45 ConfigPath::Project(path) => {
46 info!("Project config loaded: {}", path.display())
47 }
48 _ => (),
49 }
50 check_for_unknown_keys(path.to_path_buf(), &cfg);
51 } else {
52 info!("Using default {} config", cfg.chain());
53 }
54 if opts.dry_run {
55 return Ok(());
56 }
57
58 let rt = tokio::runtime::Builder::new_multi_thread()
59 .enable_all()
60 .build()?;
61
62 for task in background_tasks {
63 rt.spawn(task);
64 }
65
66 let ret = rt.block_on(super::start_interruptable(opts, cfg));
67 info!("Shutting down tokio...");
68 rt.shutdown_timeout(Duration::from_secs_f32(0.5));
69 info!("Forest finish shutdown");
70 ret
71}