1use std::ffi::OsString;
10use std::path::{Path, PathBuf};
11
12pub const LEGACY_ENV_PREFIX: &str = "AV_";
14pub const ENV_PREFIX: &str = "PV_";
16
17pub fn legacy_env_adoptions<I>(vars: I) -> Vec<(OsString, OsString)>
23where
24 I: IntoIterator<Item = (OsString, OsString)>,
25{
26 let vars: Vec<(OsString, OsString)> = vars.into_iter().collect();
27 let present = |name: &OsString| vars.iter().any(|(key, value)| key == name && !value.is_empty());
28 let mut adopt = Vec::new();
29 for (key, value) in &vars {
30 let Some(name) = key.to_str() else { continue };
31 let Some(rest) = name.strip_prefix(LEGACY_ENV_PREFIX) else {
32 continue;
33 };
34 if rest.is_empty() {
35 continue;
36 }
37 let new_name = OsString::from(format!("{ENV_PREFIX}{rest}"));
38 if !present(&new_name) && !adopt.iter().any(|(n, _): &(OsString, OsString)| *n == new_name) {
39 adopt.push((new_name, value.clone()));
40 }
41 }
42 adopt
43}
44
45pub fn adopt_legacy_env() {
52 let adoptions = legacy_env_adoptions(std::env::vars_os());
53 if adoptions.is_empty() {
54 return;
55 }
56 let names: Vec<String> = adoptions
57 .iter()
58 .map(|(name, _)| name.to_string_lossy().into_owned())
59 .collect();
60 for (name, value) in adoptions {
61 std::env::set_var(name, value);
62 }
63 eprintln!(
64 "warning: read {} from the pre-rename AV_* variables; rename them to PV_* (the AV_* names will be removed in a later release)",
65 names.join(", ")
66 );
67}
68
69pub fn existing_or_legacy(current: PathBuf, legacy: &Path) -> PathBuf {
74 if !current.exists() && legacy.exists() {
75 legacy.to_path_buf()
76 } else {
77 current
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 #![allow(clippy::unwrap_used)]
84 use super::*;
85
86 fn pairs(list: &[(&str, &str)]) -> Vec<(OsString, OsString)> {
87 list.iter()
88 .map(|(k, v)| (OsString::from(k), OsString::from(v)))
89 .collect()
90 }
91
92 #[test]
93 fn adopts_only_unset_new_names() {
94 let got = legacy_env_adoptions(pairs(&[
95 ("AV_CONFIG", "/etc/old.toml"),
96 ("AV_CONSOLE_TOKEN", "old-token"),
97 ("PV_CONSOLE_TOKEN", "new-token"),
98 ("AV_", "ignored"),
99 ("AV_LISTEN", "0.0.0.0:8484"),
100 ("PV_LISTEN", ""),
101 ("HOME", "/root"),
102 ]));
103 assert_eq!(
104 got,
105 pairs(&[("PV_CONFIG", "/etc/old.toml"), ("PV_LISTEN", "0.0.0.0:8484")])
106 );
107 }
108
109 #[test]
110 fn falls_back_to_the_legacy_file_only_when_the_new_one_is_missing() {
111 let dir = tempfile::tempdir().unwrap();
112 let current = dir.path().join("proofvisor.toml");
113 let legacy = dir.path().join("agentvisor.toml");
114 assert_eq!(existing_or_legacy(current.clone(), &legacy), current);
115 std::fs::write(&legacy, "").unwrap();
116 assert_eq!(existing_or_legacy(current.clone(), &legacy), legacy);
117 std::fs::write(¤t, "").unwrap();
118 assert_eq!(existing_or_legacy(current.clone(), &legacy), current);
119 }
120}