use crate::{RED, BLUE, WARN, RESET, GRAY}; use std::{env, error::Error, fs, io, path::PathBuf, process::exit}; use home::home_dir; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Model { // outdated Claude12, GPT35, GPT3, // current Claude, GPT4OMini, Llama, Mixtral } impl ToString for Model { fn to_string(&self) -> String { match self { Self::Claude12 => panic!("Your config is outdated! Please delete your ~/.config/hey directory"), Self::GPT35 => panic!("Your config is outdated! Please delete your ~/.config/hey directory"), Self::Claude => String::from("claude-3-haiku-20240307"), Self::GPT3 => String::from("gpt-3.5-turbo-0125"), Self::Llama => String::from("meta-llama/Llama-3-70b-chat-hf"), Self::Mixtral => String::from("mistralai/Mixtral-8x7B-Instruct-v0.1"), Self::GPT4OMini => String::from("gpt-4o-mini") } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { pub model: Model, pub tos: bool } impl Default for Config { fn default() -> Self { Self { model: Model::Claude, tos: false } } } impl Config { pub fn set_tos(&mut self, tos: bool) -> Result<(), Box> { self.tos = tos; self.save()?; Ok(()) } pub fn check_tos(&self) -> () { if ! self.tos { eprintln!("{RED}You need to agree to DuckDuckgo AI Chat TOS to continue.{RESET}"); eprintln!("{GRAY}Visit it on this URL: {RESET}{BLUE}https://duck.ai{RESET}"); eprintln!(); eprintln!("{GRAY}If it doesnt pop up, open the URL in a private window.{RESET}"); eprintln!("{GRAY}Running this command is equivalent to clicking \"I Agree\" in the ToS window:{RESET}"); eprintln!("{BLUE} {} --agree-tos{RESET}", std::env::current_exe().unwrap().display()); eprintln!(); eprintln!("{WARN}Note: if you want to, modify `tos` parameter in {}{RESET}", Config::get_path::().join(Config::get_file_name::()).display()); exit(3); } } pub fn get_path>() -> T { match env::var("HEY_CONFIG_PATH") { Ok(v) => v, Err(_) => match home_dir() { Some(home) => home.join(".config/hey").as_os_str().as_encoded_bytes().iter().map(|x| char::from(*x)).collect::(), None => panic!("Cannot detect your home directory!") } }.into() } pub fn get_file_name>() -> T { match env::var("HEY_CONFIG_FILENAME") { Ok(v) => v, Err(_) => "conf.toml".into() }.into() } fn ensure_dir_exists() -> io::Result<()> { let path: PathBuf = Self::get_path(); if ! path.is_dir() { fs::create_dir_all(path)? } Ok(()) } pub fn save(self: &Self) -> Result<(), Box> { let path = Self::get_path::(); Self::ensure_dir_exists()?; let file_path = path.join(Self::get_file_name::()); fs::write(file_path, toml::to_string_pretty(self)?)?; Ok(()) } pub fn load() -> Result> { let path = Self::get_path::(); let file_path = path.join(Self::get_file_name::()); if ! file_path.is_file() { Ok(Self::default()) } else { let conf: Config = toml::from_str(&fs::read_to_string(file_path)?)?; conf.model.to_string(); // so that it would panic if the config is outdated Ok(conf) } } }