so6 0.2.0

Framework for manage background data migration with PostgreSQL
Documentation
use clap::{ArgAction, Parser, Subcommand};
use dirs;
use serde::Deserialize;
use so6;
use std::env::current_dir;
use std::fs;
use std::io::{stdin, stdout, Write};
use std::path::PathBuf;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    #[serde(default = "default_workdir")]
    workdir: PathBuf,
    #[serde(default = "default_tasks")]
    tasks: PathBuf,
}

fn default_workdir() -> PathBuf {
    current_dir().unwrap()
}

fn default_tasks() -> PathBuf {
    "**/so6.yml".into()
}

#[derive(Parser, Debug)]
#[clap(version)]
struct Args {
    /// project directory where so6 work has to be done (default is current directory )
    #[arg(short, long, env = "SO6_WORKDIR")]
    workdir: Option<String>,

    /// glob style path where all taks definition files (default is **/so6.yml)
    #[arg(short, long, env = "SO6_TASKS")]
    tasks: Option<String>,

    /// Config file location default is ~/.config/so6.yml
    /// All so6 parameters may be defined in config_file
    #[arg(short, long)]
    config: Option<String>,

    #[command(subcommand)]
    cmd: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
    /// List all available migration tasks in migrations_path
    List {},
    /// Configure migration tasks (delay before + nb lines per call)
    Configure {},
    /// Execute one or all tasks
    Execute {
        /// Execute only for one task
        migration_name: Option<String>,
    },
    /// Enter interactive prompt to create indexes one by one
    Indexes {},
    Index {
        /// Index file path in project
        path: PathBuf,
        /// Index key to find index specification to apply
        key: String,
        /// Index creation lock
        #[arg(long, action=ArgAction::SetTrue)]
        locked: bool,
    },
}

fn get_config(config: Option<String>, workdir: Option<String>, tasks: Option<String>) -> Config {
    let config_path = match config {
        None => dirs::config_dir().unwrap().join("so6.yml"),
        Some(path_string) => PathBuf::from(path_string),
    };
    let mut computed_config: Config = serde_yaml::from_str(
        match fs::read_to_string(config_path.to_str().unwrap()) {
            Ok(content) => content,
            Err(_) => String::from("{}"),
        }
        .as_str(),
    )
    .expect(format!("Couldn't deserialize {}", config_path.to_str().unwrap()).as_str());

    if let Some(wd) = workdir {
        computed_config.workdir = wd.into();
    }
    if let Some(t) = tasks {
        computed_config.tasks = t.into();
    }

    computed_config
}

fn main() {
    let args = Args::parse();
    let config = get_config(args.config, args.workdir, args.tasks);
    match args.cmd {
        Commands::List {} => {
            println!("list tasks");
        }
        Commands::Configure {} => {
            println!("configure tasks {}", config.workdir.to_str().unwrap());
        }
        Commands::Execute { migration_name } => {
            println!("Execute tasks {}", migration_name.unwrap());
        }
        Commands::Indexes {} => {
            let indexes = so6::list(config.workdir, config.tasks);
            for (counter, (path, name, _)) in indexes.iter().enumerate() {
                println!("{} {}: {}", path.to_str().unwrap(), counter, name);
            }
            print!("Select the one you want to create: ");
            let _ = stdout().flush();
            let mut response = String::new();
            stdin().read_line(&mut response).unwrap();
            let id: usize = response
                .as_str()
                .trim()
                .parse()
                .expect("A number was expected!");
            let (selected_index_file_path, selected_name, selected_index) = indexes[id].clone();
            println!(
                "You selected {selected_index_file_path:?} {selected_name}: {selected_index:?}"
            );
            println!("{}", selected_index.background_run());
        }
        Commands::Index { path, key, locked } => {
            so6::create_index(path, key, locked);
        }
    }
}