gpt_commit 0.1.2

A tool to make ChatGPT create a commit message based on a `git diff`.
Documentation
use crate::argument::Option;
use crate::constants::{CONFIG_FILE_NAME, DEFAULT_MODEL, DEFAULT_OPEN_AI_URL};
use colored::*;
use serde::{Deserialize, Serialize};
use std::env;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use std::path::{Path, PathBuf};

#[derive(Serialize, Deserialize)]
pub struct Config {
    api_key: String,
    language: String,
    url: String,
    model: String,
}

impl Config {
    pub fn load() -> Result<Self, String> {
        let config_path = Self::get_config_path();
        println!("{}", config_path.display());
        if config_path.exists() {
            Self::read_config(config_path)
        } else {
            println!(
                "{}",
                "Config file is not found\n Create a Config file".yellow()
            );
            Self::create_config(config_path)
        }
    }

    fn create_config<P: AsRef<Path>>(path: P) -> Result<Config, String> {
        let _api_key = get_stdin("Enter your ChatGPT API Key");
        let config = Config {
            api_key: _api_key,
            language: "".to_string(),
            url: DEFAULT_OPEN_AI_URL.to_string(),
            model: DEFAULT_MODEL.to_string(),
        };
        write_config(&config, path).unwrap();
        Ok(config)
    }
    fn read_config<P: AsRef<Path>>(path: P) -> Result<Config, String> {
        let mut file = File::open(path).map_err(|e| e.to_string())?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)
            .map_err(|e| e.to_string())?;
        serde_yaml::from_str(&contents).map_err(|e| e.to_string())
    }

    pub fn language(&self) -> String {
        self.language.clone()
    }

    pub fn api_key(&self) -> String {
        self.api_key.clone()
    }

    pub fn url(&self) -> String {
        self.url.clone()
    }

    pub fn model(&self) -> String {
        self.model.clone()
    }

    pub fn update_config(&mut self, op: &Option, value: String) {
        let mut val = value;

        if val.is_empty() {
            let _ = &self.display_current_value(op);
            val = get_stdin(&op.to_string());
        }

        match &op {
            Option::Language => self.language = val,
            Option::ApiKey => self.api_key = val,
            Option::URL => self.url = val,
            Option::Model => self.model = val,
            _ => {}
        }
        write_config(self, Self::get_config_path()).unwrap();
    }
    pub fn display(&self) {
        println!("{}", "Config".green());
        println!("{}: {}", "API Key".green(), self.api_key.green());
        println!("{}: {}", "Language".green(), self.language.green());
        println!("{}: {}", "URL".green(), self.url.green());
        println!("{}: {}", "Model".green(), self.model.green());
    }

    fn display_current_value(&self, op: &Option) {
        let val = match &op {
            Option::Language => self.language(),
            Option::ApiKey => self.api_key(),
            Option::URL => self.url(),
            Option::Model => self.model(),
            _ => "".to_string(),
        };
        println!("The current setting is {}", val);
    }

    fn get_config_path() -> PathBuf {
        let executable_path = env::current_exe().expect("Failed to get executable path");
        let install_dir = executable_path
            .parent()
            .expect("Failed to get install directory");

        PathBuf::from(install_dir).join(CONFIG_FILE_NAME)
    }
}
fn get_stdin(text: &str) -> String {
    println!("{} {}", "Please enter".blue(), text.blue());
    let mut str = String::new();
    std::io::stdin().read_line(&mut str).unwrap();
    str.trim().to_string()
}

fn write_config<P: AsRef<Path>>(config: &Config, path: P) -> Result<(), String> {
    let contents = serde_yaml::to_string(config).map_err(|e| e.to_string())?;
    let mut file = OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)
        .open(path)
        .map_err(|e| e.to_string())?;

    file.write_all(contents.as_bytes())
        .map_err(|e| e.to_string())
}