rustauth-cli 0.2.0

Command-line tools for RustAuth.
Documentation
use std::fs;

use crate::app::{AppContext, AppError, PluginChangeArgs, PluginListArgs};
use crate::config::CliConfig;
use crate::db;
use crate::output::print_json;
use crate::plugins::{is_official_plugin, official_plugins, rust_snippet};
use crate::prompt::confirm;

pub fn list(args: PluginListArgs) -> Result<(), AppError> {
    let plugins = official_plugins();
    if args.json {
        print_json(&plugins)?;
    } else {
        for plugin in plugins {
            let schema = if plugin.schema_supported {
                "schema"
            } else {
                "no schema"
            };
            println!("{} ({schema})", plugin.id);
        }
    }
    Ok(())
}

pub async fn add(context: &AppContext, args: PluginChangeArgs) -> Result<(), AppError> {
    if !is_official_plugin(&args.plugin) {
        return Err(AppError::Message(format!(
            "`{}` is not an official RustAuth plugin.",
            args.plugin
        )));
    }
    let _ = context.load_config()?;
    let path = context.config_path();
    let source = fs::read_to_string(path).map_err(|source| AppError::Io {
        context: format!("failed to read {}", path.display()),
        source,
    })?;
    let updated = CliConfig::add_plugin_to_document(&source, &args.plugin)?;
    if !confirm(
        &format!("Add `{}` to {}?", args.plugin, path.display()),
        args.yes,
    )? {
        return Err(AppError::Message("Plugin update aborted.".to_owned()));
    }
    fs::write(path, updated).map_err(|source| AppError::Io {
        context: format!("failed to write {}", path.display()),
        source,
    })?;
    println!("Added plugin `{}` to {}.", args.plugin, path.display());
    if let Some(snippet) = rust_snippet(&args.plugin) {
        println!("Rust snippet: {snippet}");
    }
    let config = context.load_config()?;
    match db::plan_with_base(&config, false, Some(context.cwd())).await {
        Ok(plan) if !plan.plan.is_empty() => {
            println!("This plugin changes the database schema.");
            println!("Run `rustauth db generate` or `rustauth db migrate`.");
        }
        Ok(_) => {}
        Err(db::DbCliError::UnsupportedAdapter(_))
        | Err(db::DbCliError::UnsupportedProvider(_)) => {
            println!("Database impact check skipped for non-SQL configuration.");
        }
        Err(error) => {
            println!("Database impact could not be checked: {error}");
        }
    }
    Ok(())
}

pub fn remove(context: &AppContext, args: PluginChangeArgs) -> Result<(), AppError> {
    let _ = context.load_config()?;
    let path = context.config_path();
    let source = fs::read_to_string(path).map_err(|source| AppError::Io {
        context: format!("failed to read {}", path.display()),
        source,
    })?;
    let updated = CliConfig::remove_plugin_from_document(&source, &args.plugin)?;
    if !confirm(
        &format!("Remove `{}` from {}?", args.plugin, path.display()),
        args.yes,
    )? {
        return Err(AppError::Message("Plugin update aborted.".to_owned()));
    }
    fs::write(path, updated).map_err(|source| AppError::Io {
        context: format!("failed to write {}", path.display()),
        source,
    })?;
    println!("Removed plugin `{}` from {}.", args.plugin, path.display());
    println!("RustAuth does not generate destructive migrations in v1.");
    Ok(())
}