zinc-wallet-cli 0.4.0

Agent-first Bitcoin + Ordinals CLI wallet with account-based taproot ordinals + native segwit payment addresses (optional human mode)
use crate::cli::{Cli, SetupArgs};
#[cfg(feature = "ui")]
mod state;
#[cfg(feature = "ui")]
mod tui;
use crate::error::AppError;
use zeroize::Zeroizing;

#[derive(Debug, Clone)]
pub struct SetupValues {
    pub profile: String,
    pub data_dir: Option<String>,
    pub password_env: String,
    pub default_network: Option<String>,
    pub default_scheme: Option<String>,
    pub default_payment_address_type: Option<String>,
    pub default_esplora_url: Option<String>,
    pub default_ord_url: Option<String>,
    pub default_pulse_url: Option<String>,
    pub initialize_wallet: bool,
    pub restore_mnemonic: Option<String>,
    pub words: Option<u8>,
    pub password: Option<Zeroizing<String>>,
}

pub(crate) fn resolve_setup_values(cli: &Cli, args: &SetupArgs) -> Result<SetupValues, AppError> {
    let profile = args
        .profile
        .clone()
        .unwrap_or_else(|| cli.profile.clone().unwrap_or_else(|| "default".to_string()));
    let data_dir = args
        .data_dir
        .as_ref()
        .map(|path: &std::path::PathBuf| path.display().to_string());
    let password_env = args.password_env.clone().unwrap_or_else(|| {
        cli.password_env
            .clone()
            .unwrap_or_else(|| "ZINC_WALLET_PASSWORD".to_string())
    });
    let default_network = args.default_network.clone().or(cli.network.clone());
    let default_scheme = args.default_scheme.clone().or(cli.scheme.clone());
    let default_payment_address_type = args
        .default_payment_address_type
        .clone()
        .or(cli.payment_address_type.clone());
    let default_esplora_url = args.default_esplora_url.clone().or(cli.esplora_url.clone());
    let default_ord_url = args.default_ord_url.clone().or(cli.ord_url.clone());
    let default_pulse_url = args.default_pulse_url.clone().or(cli.pulse_url.clone());

    Ok(SetupValues {
        profile,
        data_dir,
        password_env,
        default_network,
        default_scheme,
        default_payment_address_type,
        default_esplora_url,
        default_ord_url,
        default_pulse_url,
        initialize_wallet: args.restore_mnemonic.is_some() || args.words.is_some(),
        restore_mnemonic: args.restore_mnemonic.clone(),
        words: args.words,
        password: None,
    })
}

pub(crate) fn should_run_setup_wizard(
    args: &SetupArgs,
    cli: &Cli,
    stdin_is_tty: bool,
    stdout_is_tty: bool,
) -> bool {
    #[cfg(not(feature = "ui"))]
    {
        let _ = (args, cli, stdin_is_tty, stdout_is_tty);
        return false;
    }

    #[cfg(feature = "ui")]
    {
        // Check if SetupArgs is empty
        let is_empty = args.profile.is_none()
            && args.data_dir.is_none()
            && args.password_env.is_none()
            && args.default_esplora_url.is_none()
            && args.default_ord_url.is_none()
            && args.default_pulse_url.is_none()
            && args.default_payment_address_type.is_none()
            && args.restore_mnemonic.is_none()
            && args.words.is_none();

        is_empty && !cli.agent && stdin_is_tty && stdout_is_tty
    }
}

#[cfg(feature = "ui")]
pub(crate) async fn run_tui_setup_wizard(seed: SetupValues) -> Result<SetupValues, AppError> {
    let state = state::SetupState::new(seed);
    let wizard = tui::TuiWizard::new(state)?;
    let final_state = wizard.run().await?;
    Ok(final_state.values)
}

#[cfg(not(feature = "ui"))]
pub(crate) async fn run_tui_setup_wizard(_seed: SetupValues) -> Result<SetupValues, AppError> {
    Err(AppError::Invalid(
        "setup wizard requires a zinc-cli build with the `ui` feature enabled".to_string(),
    ))
}