nethermit 0.1.4

A CLI tool to convert Jsonnet to YAML
Documentation
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use std::{
    fs,
    io::{self, Read},
    path::{Path, PathBuf},
};
use tabled::Table;

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

#[derive(Subcommand)]
enum Commands {
    /// Install resources from a Jsonnet file
    Install {
        /// The Jsonnet file to process
        file: Option<PathBuf>,

        /// Name of the set to install
        #[arg(long, required = true)]
        set: String,
    },

    /// List installed sets
    List,

    /// Delete a set and all its resources
    Delete {
        /// Name of the set to delete
        name: String,
    },
}

fn process_jsonnet_file(path: &PathBuf) -> Result<String> {
    let content = fs::read_to_string(path)
        .with_context(|| format!("Failed to read file: {}", path.display()))?;

    // Add the parent directory of the input file to the import paths
    let import_paths = if let Some(parent) = path.parent() {
        vec![parent.to_path_buf()]
    } else {
        vec![]
    };

    nethermit::jsonnet_to_yaml(&content, &import_paths)
}

fn process_stdin() -> Result<String> {
    let mut buffer = String::new();
    io::stdin()
        .read_to_string(&mut buffer)
        .context("Failed to read from stdin")?;

    // When reading from stdin, use current directory for imports
    let import_paths = vec![PathBuf::from(".")];
    nethermit::jsonnet_to_yaml(&buffer, &import_paths)
}

#[tokio::main]
async fn main() -> Result<()> {
    let cli = Cli::parse();
    let client = nethermit::RealKubeClient::new().await?;

    match cli.command {
        Commands::Install { file, set } => {
            // Check if set already exists
            if nethermit::set_exists(&client, &set).await? {
                return Err(anyhow::anyhow!(
                    "{}",
                    nethermit::format_error(&format!("Set '{}' already exists", set))
                ));
            }

            // Process input
            let yaml = match file {
                Some(ref path) if path == Path::new("-") => process_stdin()?,
                Some(ref path) => process_jsonnet_file(path)?,
                None => process_stdin()?,
            };

            println!(
                "{}",
                nethermit::format_info("Generated YAML configuration:")
            );
            println!("{yaml}");

            println!(
                "{}",
                nethermit::format_info("Applying configuration to Kubernetes cluster...")
            );
            let pb = nethermit::create_spinner("Installing resources");
            nethermit::apply_kubernetes(&yaml, Some(&set)).await?;
            pb.finish_with_message(nethermit::format_success(&format!(
                "Successfully installed set '{}'",
                set
            )));
        }

        Commands::List => {
            let sets = nethermit::list_sets_info(&client).await?;
            if sets.is_empty() {
                println!("{}", nethermit::format_info("No sets installed"));
            } else {
                println!("{}", nethermit::format_info("Installed sets:"));
                let table = Table::new(sets).to_string();
                println!("{table}");
            }
        }

        Commands::Delete { name } => {
            let pb = nethermit::create_spinner(&format!("Deleting resources for set '{}'", name));
            match nethermit::delete_set_resources(&client, &name).await {
                Ok(_) => {
                    pb.finish_with_message(nethermit::format_success(&format!(
                        "Successfully deleted set '{}'",
                        name
                    )));
                }
                Err(e) => {
                    pb.finish_with_message(nethermit::format_error(&format!(
                        "Failed to delete set '{}': {}",
                        name, e
                    )));
                    return Err(e);
                }
            }
        }
    }

    Ok(())
}