smartcat 1.4.0

Putting a brain behind `cat`. CLI interface to bring language models in the Unix ecosystem 🐈‍⬛
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
use std::path::PathBuf;

const VOICE_CONFIG_FILE: &str = "voice.toml";
pub const AUDIO_FILE_PATH_PLACEHOLDER: &str = "<audio_file_path_placeholder>";

use super::{api::Api, resolve_config_path};

#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct VoiceConfig {
    pub url: String,
    pub recording_command: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    pub api: Api,
}

impl Default for VoiceConfig {
    fn default() -> Self {
        let recording_command: String = match std::env::consts::OS {
            "windows" => format!("sox -t waveaudio 0 -d {}", AUDIO_FILE_PATH_PLACEHOLDER),
            "macos" => format!("sox -t waveaudio 0 -d {}", AUDIO_FILE_PATH_PLACEHOLDER),
            "linux" => format!("arecord -f S16_LE --quiet {}", AUDIO_FILE_PATH_PLACEHOLDER),
            os => panic!("Unexpected os: {}", os),
        };

        VoiceConfig {
            url: "https://api.openai.com/v1/audio/transcriptions".to_string(),
            recording_command,
            model: Some("whisper-1".to_string()),
            api: Api::Openai,
        }
    }
}

pub(super) fn voice_config_path() -> PathBuf {
    resolve_config_path().join(VOICE_CONFIG_FILE)
}

pub(super) fn generate_voice_file() -> std::io::Result<()> {
    let voice_config = VoiceConfig::default();

    std::fs::create_dir_all(voice_config_path().parent().unwrap())?;

    let mut voice_config_file = fs::File::create(voice_config_path())?;

    let voice_config_str = toml::to_string_pretty(&voice_config)
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
    voice_config_file.write_all(voice_config_str.as_bytes())?;
    Ok(())
}

pub fn get_voice_config() -> VoiceConfig {
    let content = fs::read_to_string(voice_config_path()).unwrap_or_else(|error| {
        panic!("Could not read file {:?}, {:?}", voice_config_path(), error)
    });

    toml::from_str(&content).expect("Unble to parse voice file content into config struct")
}