peprs-cli 0.2.0

Command-line tool for validating, inspecting, and converting PEP projects
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,
}

#[derive(clap::ValueEnum, Clone)]
pub enum ConvertFormat {
    /// YAML format
    Yaml,
    /// JSON format
    Json,
    /// CSV format
    Csv,
}

// #[derive(clap::ValueEnum, Clone)]
// pub enum WdlConvertFormat {
//     /// WDL (Workflow Description Language) format
//     Wdl,
// }

#[derive(Subcommand)]
pub enum Commands {
    /// PEPHub Client, for managing PEPHub projects
    Client {
        #[command(subcommand)]
        command: PHC,
    },

    /// Inspect project
    Inspect {
        /// Path to the project configuration yaml file.
        path: String,

        /// Optional name parameter
        #[arg(short = 'n', long = "sample-name")]
        name: Option<String>,

        /// Sample table index to use (default: "sample_name")
        #[arg(long = "st-index")]
        st_index: Option<String>,

        /// Subsample table index to use
        #[arg(long = "sst-index")]
        sst_index: Option<String>,

        /// Names of the amendments to activate
        #[arg(long = "amendments", num_args = 1..)]
        amendments: Option<Vec<String>>,
    },

    /// Validate a project against an eido schema
    Validate {
        /// Path to the project configuration yaml file.
        path: String,

        /// Path to the eido schema file (YAML or JSON).
        #[arg(short = 's', long = "schema")]
        schema: String,

        /// Name of the sample to validate. Only this sample will be validated.
        #[arg(short = 'n', long = "sample-name")]
        sample_name: Option<String>,

        /// Sample table index to use (default: "sample_name")
        #[arg(long = "st-index")]
        st_index: Option<String>,

        /// Subsample table index to use
        #[arg(long = "sst-index")]
        sst_index: Option<String>,

        /// Names of the amendments to activate
        #[arg(long = "amendments", num_args = 1..)]
        amendments: Option<Vec<String>>,
    },

    /// Convert PEP project to a different format (yaml, json, csv)
    Convert {
        /// Path to the project configuration yaml file.
        path: String,

        /// Output format: yaml, json, csv
        #[arg(value_enum, short = 'f', long = "format")]
        format: ConvertFormat,

        /// Output file path. If not provided, prints to stdout (only if < 100 samples).
        #[arg(short = 'p', long = "path")]
        output_path: Option<String>,

        /// Sample table index to use (default: "sample_name")
        #[arg(long = "st-index")]
        st_index: Option<String>,

        /// Subsample table index to use
        #[arg(long = "sst-index")]
        sst_index: Option<String>,

        /// Names of the amendments to activate
        #[arg(long = "amendments", num_args = 1..)]
        amendments: Option<Vec<String>>,
    },
    // /// Convert samples into WDL input format (deprecated)
    // ConvertWdl {
    //     path: String,
    //     schema: String,
    //     #[arg(value_enum, short = 'f', long = "to")]
    //     format: WdlConvertFormat,
    //     #[arg(short = 'n', long = "name")]
    //     name: Option<String>,
    //     #[arg(long = "nested-inputs")]
    //     nested_inputs: Option<bool>,
    //     #[arg(long = "show-non-literals")]
    //     show_non_literals: Option<bool>,
    //     #[arg(long = "hide-defaults")]
    //     hide_defaults: Option<bool>,
    // },
}

#[derive(Subcommand)]
pub enum PHC {
    /// Login
    Login {
        /// Token generated by PEPhub
        #[arg(long = "token")]
        token: Option<String>,

        /// Base URL for PEPhub, if different not main PEPhub being used.
        #[arg(long = "url")]
        url: Option<String>,
    },
    /// Logout from current PEPhub
    Logout {},

    /// Pull project from PEPHub and save it locally.
    Pull {
        /// PEPHub registry path, e.g. namespace/name:tag
        registry: String,

        /// Output folder (or .zip file path when --zip)
        #[arg(short = 'p', long = "path")]
        path: String,

        /// Save as a single .zip archive instead of a folder
        #[arg(long = "zip", default_value_t = false)]
        zip: bool,
    },
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_my_subcommand() {
        let args = vec![
            "peprs",
            "client",
            "pull",
            "registry/project:default",
            "-p",
            "./out",
        ];
        let cli = Cli::try_parse_from(args).expect("Parsing failed");

        match cli.command {
            Commands::Client { command } => match command {
                PHC::Pull {
                    registry,
                    path,
                    zip,
                } => {
                    assert_eq!(registry, "registry/project:default");
                    assert_eq!(path, "./out");
                    assert!(!zip);
                }
                _ => unreachable!(),
            },
            _ => panic!("Expected Commands::Client"),
        }
    }
}