cinchdb 0.2.0

CLI for CinchDB - database and scope management
//! Cinch CLI - database and scope management for CinchDB

mod client;
mod commands;
mod config;
mod output;

use anyhow::Result;
use clap::{Parser, Subcommand};
use commands::{auth, config as config_cmd, db, env, org, scope};

/// CinchDB CLI - manage databases, scopes, and organizations
#[derive(Parser)]
#[command(name = "cinch", version, about, propagate_version = true)]
struct Cli {
    /// Output format (default: human-readable)
    #[arg(long, global = true)]
    json: bool,

    /// API base URL override
    #[arg(long, global = true, env = "CINCH_API_URL")]
    api_url: Option<String>,

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

#[derive(Subcommand)]
enum Commands {
    /// Authentication: login, logout, whoami, API keys
    Auth {
        #[command(subcommand)]
        command: auth::AuthCommands,
    },

    /// Database operations: create, list, show, shell, destroy
    Db {
        #[command(subcommand)]
        command: db::DbCommands,
    },

    /// Scope management: create, list, show, destroy, token
    Scope {
        #[command(subcommand)]
        command: scope::ScopeCommands,
    },

    /// Organization management: list, switch, members
    Org {
        #[command(subcommand)]
        command: org::OrgCommands,
    },

    /// Show or set persistent configuration
    Config {
        #[command(subcommand)]
        command: Option<config_cmd::ConfigCommands>,
    },

    /// Print eval-able environment variables for a database
    Env {
        /// Database name
        name: String,

        /// Scope (defaults to context scope)
        #[arg(long)]
        scope: Option<String>,
    },

    /// Open quickstart docs in browser
    Quickstart,

    /// Self-update the CLI
    Update,
}

#[tokio::main]
async fn main() -> Result<()> {
    let cli = Cli::parse();
    let json_output = cli.json;

    let result = run(cli).await;

    if let Err(ref e) = result {
        if json_output {
            let err = serde_json::json!({ "error": format!("{e:#}") });
            eprintln!("{}", serde_json::to_string_pretty(&err).expect("json serialization failed"));
        } else {
            eprintln!("{}: {e:#}", colored::Colorize::red("error"));
        }
        std::process::exit(1);
    }

    Ok(())
}

async fn run(cli: Cli) -> Result<()> {
    let api_url = match cli.api_url {
        Some(url) => url,
        None => config::resolve_api_url()?,
    };

    match cli.command {
        Commands::Auth { command } => auth::run(command, &api_url, cli.json).await,
        Commands::Db { command } => db::run(command, &api_url, cli.json).await,
        Commands::Scope { command } => scope::run(command, &api_url, cli.json).await,
        Commands::Org { command } => org::run(command, &api_url, cli.json).await,
        Commands::Config { command } => config_cmd::run(command, cli.json),
        Commands::Env { name, scope } => env::run(&name, scope.as_deref(), &api_url, cli.json).await,
        Commands::Quickstart => {
            open::that("https://cinchdb.dev/docs/quickstart")?;
            println!("Opened quickstart docs in your browser.");
            Ok(())
        }
        Commands::Update => {
            println!("Self-update not yet implemented.");
            Ok(())
        }
    }
}