use duct::cmd;
use shell_escape::escape;
use std::borrow::Cow;
use std::path::PathBuf;
pub fn find_exomonad_binary() -> PathBuf {
if let Ok(exe) = std::env::current_exe() {
if let Some(dir) = exe.parent() {
let candidate = dir.join("exomonad");
if candidate.exists() {
return candidate;
}
}
}
if let Ok(path) = cmd!("which", "exomonad").read() {
let path = path.trim();
if !path.is_empty() {
return PathBuf::from(path);
}
}
PathBuf::from("exomonad")
}
pub fn shell_quote(s: &str) -> Cow<'_, str> {
escape(Cow::Borrowed(s))
}
pub fn build_prompt(prompt: &str, inject_context: Option<&str>) -> String {
match inject_context {
Some(ctx) => format!("{}\n\n{}", ctx, prompt),
None => prompt.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_prompt_without_context() {
let result = build_prompt("do the thing", None);
assert_eq!(result, "do the thing");
}
#[test]
fn test_build_prompt_with_context() {
let result = build_prompt("continue working", Some("CONTEXT: file.rs was modified"));
assert_eq!(result, "CONTEXT: file.rs was modified\n\ncontinue working");
}
#[test]
fn test_build_prompt_with_multiline_context() {
let ctx = "CONTEXT:\n- file1.rs modified\n- file2.rs added";
let result = build_prompt("proceed", Some(ctx));
assert_eq!(
result,
"CONTEXT:\n- file1.rs modified\n- file2.rs added\n\nproceed"
);
}
#[test]
fn test_build_prompt_with_special_chars() {
let ctx = "CONTEXT: user said \"hello\" & 'goodbye'";
let result = build_prompt("continue", Some(ctx));
assert!(result.contains("\"hello\""));
assert!(result.contains("&"));
assert!(result.contains("'goodbye'"));
}
#[test]
fn test_shell_quote_simple() {
let result = shell_quote("hello");
assert_eq!(result, "hello");
}
#[test]
fn test_shell_quote_with_spaces() {
let result = shell_quote("hello world");
assert_eq!(result, "'hello world'");
}
}