tuning 0.4.0

ansible-like tool with a smaller scope, focused primarily on complementing dotfiles for cross-machine bliss
#![deny(clippy::all, clippy::pedantic, unsafe_code)]

pub(crate) mod config;
pub(crate) mod convert;
pub(crate) mod env;
pub(crate) mod facts;
pub(crate) mod installer;
pub(crate) mod jobs;
pub(crate) mod preflight;
pub(crate) mod runner;
pub(crate) mod runnerstate;
pub(crate) mod status;
pub(crate) mod template;

use std::io;

use camino::Utf8PathBuf;
use clap::Parser;
use thiserror::Error as ThisError;

use crate::facts::Facts;

#[derive(Debug, Parser)]
#[command(version)]
struct Args {
    #[arg(short, long)]
    /// path to config file, instead of reading ~/.config/tuning/main.toml then
    /// ~/.dotfiles/tuning/main.toml by default
    config: Option<Utf8PathBuf>,
}

#[derive(Debug, ThisError)]
enum Error {
    #[error(transparent)]
    Config {
        #[from]
        source: config::Error,
    },
    #[error(transparent)]
    Facts {
        #[from]
        source: facts::Error,
    },
    #[error(transparent)]
    Io {
        #[from]
        source: io::Error,
    },
    #[error(transparent)]
    Job {
        #[from]
        source: jobs::Error,
    },
    #[error(transparent)]
    Preflight {
        #[from]
        source: preflight::Error,
    },
}

type Result<T> = std::result::Result<T, Error>;

#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<()> {
    let args = Args::parse();
    let mut facts = Facts::gather()?;

    let config_path = if let Some(c) = args.config {
        eprintln!("finding: {}", &c);
        if c.is_absolute() {
            c
        } else {
            crate::env::current_dir().join(c).canonicalize_utf8()?
        }
    } else {
        config::find_config_file(&facts)?
    };
    eprintln!("reading: {}", &config_path);
    facts.main_file = config_path.clone();

    let rm = config::read_main_toml(config_path)?;
    rm.validate()?;

    runner::run(rm.jobs, &facts).await;

    Ok(())
}