stt-cli 0.2.1

Speech to text Cli using Groq API and OpenAI API
// src/platform/handler.rs
// Implements a handler that bridges TextInserter with the TranscriptionResultHandler trait.

pub trait TextInserter: Send + Sync {
    fn insert_text_with_options(
        &mut self,
        text: &str,
        options: InsertOptions,
    ) -> anyhow::Result<()>;
}

#[derive(Default, Debug, Clone)]
pub struct InsertOptions {
    pub auto_capitalize: bool,
    pub auto_punctuate: bool,
    // Add more options as needed
}

use crate::transcription::result_handler::TranscriptionResultHandler;
use anyhow::Result;

pub struct PlatformTextInserterHandler {
    inserter: Box<dyn TextInserter + Send + Sync>,
}

impl PlatformTextInserterHandler {
    pub fn new(inserter: Box<dyn TextInserter + Send + Sync>) -> Self {
        Self { inserter }
    }
}

impl TranscriptionResultHandler for PlatformTextInserterHandler {
    fn handle_result(&mut self, text: &str, options: InsertOptions) -> Result<()> {
        self.inserter.insert_text_with_options(text, options)
    }
}

// Stub implementation for development/testing
pub struct StubTextInserter;

impl TextInserter for StubTextInserter {
    fn insert_text_with_options(
        &mut self,
        text: &str,
        options: InsertOptions,
    ) -> anyhow::Result<()> {
        println!(
            "[StubTextInserter] Would insert text: {} with options: {:?}",
            text, options
        );
        Ok(())
    }
}

// --- Enigo-based TextInserter Implementation ---
use enigo::{Enigo, Settings, Keyboard};

pub struct EnigoTextInserter {
    enigo: Enigo,
}

impl EnigoTextInserter {
    pub fn new() -> anyhow::Result<Self> {
        // TODO: all this extra codes does not seem to help at the moment. 
        #[cfg(target_os = "linux")]
        {
            use std::env;
            let display = env::var("DISPLAY").ok();
            let wayland = env::var("WAYLAND_DISPLAY").ok();

            match (display.as_deref(), wayland.as_deref()) {
                (Some(_), Some(_)) => {
                    // Both are set: prefer X11 (DISPLAY)
                    let orig = env::var("WAYLAND_DISPLAY").ok();
                    env::remove_var("WAYLAND_DISPLAY");
                    let enigo = Enigo::new(&Settings::default())?;
                    // Restore WAYLAND_DISPLAY for rest of app
                    if let Some(val) = orig { env::set_var("WAYLAND_DISPLAY", val); }
                    Ok(Self { enigo })
                }
                (Some(_), None) => {
                    // Only X11
                    let enigo = Enigo::new(&Settings::default())?;
                    Ok(Self { enigo })
                }
                (None, Some(_)) => {
                    // Only Wayland
                    let enigo = Enigo::new(&Settings::default())?;
                    Ok(Self { enigo })
                }
                (None, None) => {
                    anyhow::bail!("Neither DISPLAY nor WAYLAND_DISPLAY is set. Cannot select Enigo backend.");
                }
            }
        }
        #[cfg(target_os = "macos")]
        {
            let enigo = Enigo::new(&Settings::default())?;
            Ok(Self { enigo })
        }
        #[cfg(target_os = "windows")]
        {
            let enigo = Enigo::new(&Settings::default())?;
            Ok(Self { enigo })
        }
        #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
        {
            anyhow::bail!("EnigoTextInserter is not supported on this platform.");
        }
    }
}

impl TextInserter for EnigoTextInserter {
    fn insert_text_with_options(
        &mut self,
        text: &str,
        options: InsertOptions,
    ) -> anyhow::Result<()> {
        // Optionally process text with options here (auto_capitalize, auto_punctuate)
        let processed_text = text; // TODO: apply options if needed
        self.enigo.text(processed_text)?;
        Ok(())
    }
}