use std::fs;
use std::path::Path;
use crate::env_diff;
pub fn save_state(path: &Path, env_data: &str) -> std::io::Result<()> {
let mut output = String::with_capacity(env_data.len());
for entry in env_data.split('\0') {
let entry = entry.trim_start_matches('\n');
if entry.is_empty() {
continue;
}
if let Some(eq_pos) = entry.find('=') {
let key = &entry[..eq_pos];
let value = &entry[eq_pos + 1..];
if key.is_empty()
|| !key.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_')
{
continue;
}
if env_diff::should_skip_var(key) {
continue;
}
output.push_str("export ");
output.push_str(key);
output.push_str("='");
for &b in value.as_bytes() {
if b == b'\'' {
output.push_str("'\\''");
} else {
output.push(b as char);
}
}
output.push_str("'\n");
}
}
fs::write(path, output)
}
#[must_use]
pub fn state_prefix(path: &Path) -> String {
let p = path.display();
format!("[ -f '{p}' ] && source '{p}'\n")
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn save_and_read_state() {
let dir = std::env::temp_dir();
let path = dir.join("reef-test-state");
let env_data = "FOO=bar\0MY_VAR=hello world\0";
save_state(&path, env_data).unwrap();
let content = fs::read_to_string(&path).unwrap();
assert!(content.contains("export FOO='bar'"));
assert!(content.contains("export MY_VAR='hello world'"));
fs::remove_file(&path).ok();
}
#[test]
fn save_state_escapes_quotes() {
let dir = std::env::temp_dir();
let path = dir.join("reef-test-state-quotes");
let env_data = "QUOTED=it's a test\0";
save_state(&path, env_data).unwrap();
let content = fs::read_to_string(&path).unwrap();
assert!(content.contains("export QUOTED='it'\\''s a test'"));
fs::remove_file(&path).ok();
}
#[test]
fn save_state_skips_bash_internals() {
let dir = std::env::temp_dir();
let path = dir.join("reef-test-state-skip");
let env_data = "BASH_VERSION=5.2\0REAL_VAR=keep\0SHLVL=1\0";
save_state(&path, env_data).unwrap();
let content = fs::read_to_string(&path).unwrap();
assert!(!content.contains("BASH_VERSION"));
assert!(!content.contains("SHLVL"));
assert!(content.contains("export REAL_VAR='keep'"));
fs::remove_file(&path).ok();
}
#[test]
fn state_prefix_format() {
let path = Path::new("/tmp/reef-state-12345");
let prefix = state_prefix(path);
assert_eq!(
prefix,
"[ -f '/tmp/reef-state-12345' ] && source '/tmp/reef-state-12345'\n"
);
}
}