mod abort_signal; mod clipboard; mod prompt_input; mod render_prompt; mod tiktoken; pub use self::abort_signal::{create_abort_signal, AbortSignal}; pub use self::clipboard::set_text; pub use self::prompt_input::*; pub use self::render_prompt::render_prompt; pub use self::tiktoken::cl100k_base_singleton; use sha2::{Digest, Sha256}; use std::env; use std::process::Command; pub fn now() -> String { let now = chrono::Local::now(); now.to_rfc3339_opts(chrono::SecondsFormat::Secs, false) } pub fn get_env_name(key: &str) -> String { format!( "{}_{}", env!("CARGO_CRATE_NAME").to_ascii_uppercase(), key.to_ascii_uppercase(), ) } /// Split text to tokens pub fn tokenize(text: &str) -> Vec { let tokens = cl100k_base_singleton() .lock() .encode_with_special_tokens(text); let token_bytes: Vec> = tokens .into_iter() .map(|v| cl100k_base_singleton().lock().decode_bytes(vec![v])) .collect(); let mut output = vec![]; let mut current_bytes = vec![]; for bytes in token_bytes { current_bytes.extend(bytes); if let Ok(v) = std::str::from_utf8(¤t_bytes) { output.push(v.to_string()); current_bytes.clear(); } } output } /// Count how many tokens a piece of text needs to consume pub fn count_tokens(text: &str) -> usize { cl100k_base_singleton() .lock() .encode_with_special_tokens(text) .len() } pub fn light_theme_from_colorfgbg(colorfgbg: &str) -> Option { let parts: Vec<_> = colorfgbg.split(';').collect(); let bg = match parts.len() { 2 => &parts[1], 3 => &parts[2], _ => { return None; } }; let bg = bg.parse::().ok()?; let (r, g, b) = ansi_colours::rgb_from_ansi256(bg); let v = 0.2126 * r as f32 + 0.7152 * g as f32 + 0.0722 * b as f32; let light = v > 128.0; Some(light) } pub fn init_tokio_runtime() -> anyhow::Result { use anyhow::Context; tokio::runtime::Builder::new_current_thread() .enable_all() .build() .with_context(|| "Failed to init tokio") } pub fn sha256sum(input: &str) -> String { let mut hasher = Sha256::new(); hasher.update(input); let result = hasher.finalize(); format!("{:x}", result) } pub fn detect_os() -> String { let os = env::consts::OS; if os == "linux" { if let Ok(contents) = std::fs::read_to_string("/etc/os-release") { for line in contents.lines() { if let Some(id) = line.strip_prefix("ID=") { return format!("{os}/{id}"); } } } } os.to_string() } pub fn detect_shell() -> String { let os = env::consts::OS; if os == "windows" { if let Some(true) = env::var("PSModulePath") .ok() .map(|v| v.split(';').count() >= 3) { "powershell.exe".into() } else { "cmd.exe".into() } } else { env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()) } } pub fn run_command(eval_str: &str) -> anyhow::Result { let shell = detect_shell(); let mut command = Command::new(&shell); if shell == "powershell.exe" { command.arg("-Command").arg(eval_str); } else if shell == "cmd.exe" { command.arg("/c").arg(eval_str); } else { command.arg("-c").arg(eval_str); }; let status = command.status()?; Ok(status.code().unwrap_or_default()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_tokenize() { assert_eq!(tokenize("😊 hello world"), ["😊", " hello", " world"]); assert_eq!(tokenize("δΈ–η•Œ"), ["δΈ–", "η•Œ"]); } #[test] fn test_count_tokens() { assert_eq!(count_tokens("😊 hello world"), 4); } }