claudy 0.2.2

Modern multi-provider launcher for Claude CLI
use dialoguer::{Input, Password, Select, theme::ColorfulTheme};
use std::io;

use crate::ports::ui_ports::PrompterPort;

pub struct Prompter {
    _reader: std::io::BufReader<io::Stdin>,
}

impl PrompterPort for Prompter {
    fn prompt(&mut self, label: &str, default_value: &str) -> anyhow::Result<String> {
        let theme = ColorfulTheme::default();
        if !default_value.is_empty() {
            Ok(Input::<String>::with_theme(&theme)
                .with_prompt(label)
                .default(default_value.to_string())
                .interact_text()?)
        } else {
            Ok(Input::<String>::with_theme(&theme)
                .with_prompt(label)
                .interact_text()?)
        }
    }

    fn prompt_opt(&mut self, label: &str, default_value: &str) -> anyhow::Result<Option<String>> {
        let theme = ColorfulTheme::default();
        let input = Input::<String>::with_theme(&theme);
        let input = input.with_prompt(label).allow_empty(true);
        let res = if !default_value.is_empty() {
            input.default(default_value.to_string()).interact_text()
        } else {
            input.interact_text()
        };

        match res {
            Ok(value) => Ok(Some(value)),
            Err(dialoguer::Error::IO(io_err))
                if io_err.kind() == io::ErrorKind::Interrupted
                    || io_err.kind() == io::ErrorKind::UnexpectedEof =>
            {
                Ok(None)
            }
            Err(e) => Err(e.into()),
        }
    }

    fn prompt_secret(&mut self, label: &str) -> anyhow::Result<String> {
        let theme = ColorfulTheme::default();
        Ok(Password::with_theme(&theme).with_prompt(label).interact()?)
    }

    fn prompt_secret_opt(&mut self, label: &str) -> anyhow::Result<Option<String>> {
        let theme = ColorfulTheme::default();
        let res = Password::with_theme(&theme)
            .with_prompt(label)
            .allow_empty_password(true)
            .interact();

        match res {
            Ok(value) => Ok(Some(value)),
            Err(dialoguer::Error::IO(io_err))
                if io_err.kind() == io::ErrorKind::Interrupted
                    || io_err.kind() == io::ErrorKind::UnexpectedEof =>
            {
                Ok(None)
            }
            Err(e) => Err(e.into()),
        }
    }

    fn confirm(&mut self, label: &str, default_yes: bool) -> anyhow::Result<bool> {
        let theme = ColorfulTheme::default();
        Ok(dialoguer::Confirm::with_theme(&theme)
            .with_prompt(label)
            .default(default_yes)
            .interact()?)
    }

    fn confirm_opt(&mut self, label: &str, default_yes: bool) -> anyhow::Result<Option<bool>> {
        let theme = ColorfulTheme::default();
        match dialoguer::Confirm::with_theme(&theme)
            .with_prompt(label)
            .default(default_yes)
            .interact_opt()
        {
            Ok(val) => Ok(val),
            Err(dialoguer::Error::IO(io_err))
                if io_err.kind() == io::ErrorKind::Interrupted
                    || io_err.kind() == io::ErrorKind::UnexpectedEof =>
            {
                Ok(None)
            }
            Err(e) => Err(e.into()),
        }
    }

    fn select(&mut self, label: &str, items: &[String], default: usize) -> anyhow::Result<usize> {
        let theme = ColorfulTheme::default();
        Ok(Select::with_theme(&theme)
            .with_prompt(label)
            .items(items)
            .default(default)
            .interact()?)
    }

    fn select_opt(
        &mut self,
        label: &str,
        items: &[String],
        default: usize,
    ) -> anyhow::Result<Option<usize>> {
        let theme = ColorfulTheme::default();
        Ok(Select::with_theme(&theme)
            .with_prompt(label)
            .items(items)
            .default(default)
            .interact_opt()?)
    }
}

impl Prompter {
    pub fn new(_stdin: io::Stdin, _stdout: io::Stdout) -> Self {
        Prompter {
            _reader: std::io::BufReader::new(io::stdin()),
        }
    }
}