spacetimedb-cli 0.10.1

A command line interface for SpacetimeDB
Documentation
use crate::config::Config;
use clap::ArgAction::SetTrue;
use clap::ArgMatches;
use clap::{Arg, Command};
use spacetimedb::stdb_path;
use std::io::Write;
use std::path::PathBuf;

pub fn cli() -> Command {
    Command::new("local")
        .args_conflicts_with_subcommands(true)
        .subcommand_required(true)
        .subcommands(get_subcommands())
        .about("Manage local SpacetimeDB database")
}

pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
    let (cmd, subcommand_args) = args.subcommand().expect("Subcommand required");
    exec_subcommand(config, cmd, subcommand_args).await
}

fn get_subcommands() -> Vec<Command> {
    vec![Command::new("clear")
        .about("Deletes all data from all local databases")
        .arg(
            Arg::new("force")
                .long("force")
                .action(SetTrue)
                .help("Clear the database without prompting for confirmation"),
        )]
}

async fn exec_subcommand(config: Config, cmd: &str, args: &ArgMatches) -> Result<(), anyhow::Error> {
    match cmd {
        "clear" => exec_clear(config, args).await,
        unknown => Err(anyhow::anyhow!("Invalid subcommand: {}", unknown)),
    }
}

async fn exec_clear(_config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
    let force = args.get_flag("force");
    if std::env::var_os("STDB_PATH").map(PathBuf::from).is_none() {
        let mut path = dirs::home_dir().unwrap_or_default();
        path.push(".spacetime");
        std::env::set_var("STDB_PATH", path.to_str().unwrap());
    }

    let control_node_dir = stdb_path("control_node");
    let worker_node_dir = stdb_path("worker_node");
    if control_node_dir.exists() || worker_node_dir.exists() {
        if control_node_dir.exists() {
            println!("Control node database path: {}", control_node_dir.to_str().unwrap());
        } else {
            println!("Control node database path: <not found>");
        }

        if worker_node_dir.exists() {
            println!("Worker node database path: {}", worker_node_dir.to_str().unwrap());
        } else {
            println!("Worker node database path: <not found>");
        }

        if !force {
            print!("Are you sure you want to delete all data from the local database? (y/n) ");
            std::io::stdout().flush()?;
            let mut input = String::new();
            std::io::stdin().read_line(&mut input)?;
            if input.trim().to_lowercase() != "y" && input.trim().to_lowercase() != "yes" {
                println!("Aborting");
                return Ok(());
            }
        } else {
            println!("Force flag is present, skipping confirmation");
        }

        if control_node_dir.exists() {
            std::fs::remove_dir_all(&control_node_dir)?;
            println!("Deleted control node database: {}", control_node_dir.to_str().unwrap());
        }
        if worker_node_dir.exists() {
            std::fs::remove_dir_all(&worker_node_dir)?;
            println!("Deleted worker node database: {}", worker_node_dir.to_str().unwrap());
        }
    } else {
        println!("Local database not found. Nothing has been deleted.");
    }
    Ok(())
}