kcfg 0.2.1

KUBECONFIG manipulation CLI
use std::env;
use std::fs;
use std::path::Path;

use crate::common::check_directory_path;
use crate::error::KcfgError;
use crate::KUBECONFIG;
use clap::Parser;
use lazy_static::lazy_static;
use regex::Regex;

const HOME: &str = "HOME";

lazy_static! {
    static ref REGEX_FILE_SPLIT: Regex = Regex::new(r#"([\w\.-]+)(\.yaml|\.yml)$"#,).unwrap();
}

/// Returns the value if the option is `Some`, otherwise `continue` is called
macro_rules! skip_none {
    ($res:expr) => {
        match $res {
            Some(val) => val,
            None => continue,
        }
    };
}

/// Options for the `use` command
#[derive(Clone, Parser, Debug)]
pub struct UseOptions {
    /// Directory of your K8S configurations
    #[clap(short, long, default_value = "~/.kube")]
    directory: String,
    /// Filter argument
    #[clap(value_delimiter(','))]
    input: Vec<String>,
}

pub fn router(params: UseOptions) -> Result<String, KcfgError> {
    if params.input.is_empty() {
        return Err(KcfgError::MissingInput);
    }
    // Check the Path
    let path_str = if params.directory.starts_with("~/") {
        let home_var = env::var(HOME).map_err(|e| KcfgError::EnvVarError {
            source: e,
            var: HOME.to_string(),
        })?;
        params.directory.replace('~', &home_var)
    } else {
        params.directory
    };
    check_directory_path(&path_str)?;
    let path = Path::new(&path_str);

    let starting_string = params.input.join(".");
    let directories = fs::read_dir(path)?.into_iter().filter_map(|e| e.ok());

    // Checking the directories
    for directory in directories {
        let p = directory.path();
        let captured = skip_none!(REGEX_FILE_SPLIT.captures(
            p.to_str()
                .ok_or_else(|| KcfgError::InvalidPath(p.clone()))?,
        ));
        let name = skip_none!(captured.get(1)).as_str().to_string();
        let ext = skip_none!(captured.get(2)).as_str().to_string();
        if starting_string == name {
            return Ok(format!(
                "export {}={}/{}{}",
                KUBECONFIG, path_str, name, ext
            ));
        }
        if name.starts_with(&format!("{}.", starting_string)) {
            return Ok(name[starting_string.len() + 1..].to_string());
        }
    }
    Err(KcfgError::ConfigNotFound(params.input))
}

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

    #[test]
    fn should_fail_with_no_input() {
        let params = UseOptions {
            directory: "test".to_string(),
            input: vec![],
        };
        match router(params) {
            Ok(_) => panic!("Test should have failed"),
            Err(e) => {
                if !matches!(e, KcfgError::MissingInput) {
                    panic!("Test failed with wrong error");
                }
            }
        }
    }
}