use std::env;
use std::path::PathBuf;
use chrono::Local;
#[derive(Debug, Clone)]
pub struct EnvironmentContext {
pub working_directory: PathBuf,
pub platform: String,
pub os_version: Option<String>,
pub date: String,
}
impl EnvironmentContext {
pub fn gather() -> Self {
let working_directory = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let platform = env::consts::OS.to_string();
let os_version = Self::get_os_version();
let date = Local::now().format("%Y-%m-%d").to_string();
Self {
working_directory,
platform,
os_version,
date,
}
}
#[cfg(unix)]
fn get_os_version() -> Option<String> {
use std::process::Command;
let output = Command::new("uname").arg("-rs").output().ok()?;
if output.status.success() {
String::from_utf8(output.stdout)
.ok()
.map(|s| s.trim().to_string())
} else {
None
}
}
#[cfg(windows)]
fn get_os_version() -> Option<String> {
use std::process::Command;
let output = Command::new("cmd").args(["/C", "ver"]).output().ok()?;
if output.status.success() {
String::from_utf8(output.stdout)
.ok()
.map(|s| s.trim().to_string())
} else {
None
}
}
#[cfg(not(any(unix, windows)))]
fn get_os_version() -> Option<String> {
None
}
pub fn to_prompt_section(&self) -> String {
let mut lines = Vec::new();
lines.push(format!(
"Working directory: {}",
self.working_directory.display()
));
lines.push(format!("Platform: {}", self.platform));
if let Some(ref version) = self.os_version {
lines.push(format!("OS Version: {}", version));
}
lines.push(format!("Today's date: {}", self.date));
format!("<env>\n{}\n</env>", lines.join("\n"))
}
}
impl Default for EnvironmentContext {
fn default() -> Self {
Self::gather()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gather_environment() {
let ctx = EnvironmentContext::gather();
assert!(!ctx.working_directory.as_os_str().is_empty());
assert!(!ctx.platform.is_empty());
assert_eq!(ctx.date.len(), 10);
assert!(ctx.date.contains('-'));
}
#[test]
fn test_to_prompt_section() {
let ctx = EnvironmentContext {
working_directory: PathBuf::from("/test/path"),
platform: "darwin".to_string(),
os_version: Some("Darwin 25.2.0".to_string()),
date: "2026-01-31".to_string(),
};
let section = ctx.to_prompt_section();
assert!(section.starts_with("<env>"));
assert!(section.ends_with("</env>"));
assert!(section.contains("Working directory: /test/path"));
assert!(section.contains("Platform: darwin"));
assert!(section.contains("OS Version: Darwin 25.2.0"));
assert!(section.contains("Today's date: 2026-01-31"));
}
#[test]
fn test_to_prompt_section_without_os_version() {
let ctx = EnvironmentContext {
working_directory: PathBuf::from("/test/path"),
platform: "unknown".to_string(),
os_version: None,
date: "2026-01-31".to_string(),
};
let section = ctx.to_prompt_section();
assert!(!section.contains("OS Version:"));
assert!(section.contains("Platform: unknown"));
}
}