use serde_json::{json, Value};
pub fn enumerate_all_overlays() -> Vec<(String, Value)> {
let mut out: Vec<(String, Value)> = Vec::new();
crate::fusevm_bridge::with_executor(|exec| {
let alias_e = exec.alias_entries();
if !alias_e.is_empty() {
out.push(("alias".into(), entries_to_json(&alias_e)));
}
let galias_e = exec.global_alias_entries();
if !galias_e.is_empty() {
out.push(("galias".into(), entries_to_json(&galias_e)));
}
let salias_e = exec.suffix_alias_entries();
if !salias_e.is_empty() {
out.push(("salias".into(), entries_to_json(&salias_e)));
}
let opts_snap = crate::ported::options::opt_state_snapshot();
if !opts_snap.is_empty() {
let map: serde_json::Map<String, Value> = opts_snap
.iter()
.map(|(k, v)| {
(
k.clone(),
Value::String(if *v { "on" } else { "off" }.into()),
)
})
.collect();
out.push(("setopt".into(), Value::Object(map)));
}
let scalar_entries: Vec<(String, String)> =
if let Ok(tab) = crate::ported::params::paramtab().read() {
tab.iter()
.filter(|(_, pm)| pm.u_arr.is_none())
.map(|(k, pm)| (k.clone(), pm.u_str.clone().unwrap_or_default()))
.collect()
} else {
Vec::new()
};
let array_entries: Vec<(String, Vec<String>)> =
if let Ok(tab) = crate::ported::params::paramtab().read() {
tab.iter()
.filter_map(|(k, pm)| pm.u_arr.clone().map(|a| (k.clone(), a)))
.collect()
} else {
Vec::new()
};
let assoc_entries: Vec<(String, indexmap::IndexMap<String, String>)> =
if let Ok(m) = crate::ported::params::paramtab_hashed_storage().lock() {
m.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
} else {
Vec::new()
};
if !scalar_entries.is_empty() || !array_entries.is_empty() || !assoc_entries.is_empty() {
let mut params = serde_json::Map::new();
for (k, v) in scalar_entries {
params.insert(k, Value::String(v));
}
for (k, v) in array_entries {
params.insert(
k,
Value::Array(v.iter().map(|s| Value::String(s.clone())).collect()),
);
}
for (k, v) in assoc_entries {
let inner: serde_json::Map<String, Value> = v
.iter()
.map(|(ik, iv)| (ik.clone(), Value::String(iv.clone())))
.collect();
params.insert(k, Value::Object(inner));
}
out.push(("params".into(), Value::Object(params)));
}
if !exec.fpath.is_empty() {
out.push((
"fpath".into(),
Value::Array(
exec.fpath
.iter()
.map(|p| Value::String(p.display().to_string()))
.collect(),
),
));
}
let nd_snap: Vec<(String, String)> = crate::ported::hashnameddir::nameddirtab()
.lock()
.ok()
.map(|g| g.iter().map(|(k, v)| (k.clone(), v.dir.clone())).collect())
.unwrap_or_default();
if !nd_snap.is_empty() {
let map: serde_json::Map<String, Value> = nd_snap
.into_iter()
.map(|(k, v)| (k, Value::String(v)))
.collect();
out.push(("named_dir".into(), Value::Object(map)));
}
let _ = exec;
if !exec.completions.is_empty() {
let map: serde_json::Map<String, Value> = exec
.completions
.iter()
.map(|(k, v)| (k.clone(), json!(format!("{:?}", v))))
.collect();
out.push(("compdef".into(), Value::Object(map)));
}
if !exec.zstyles.is_empty() {
let arr: Vec<Value> = exec
.zstyles
.iter()
.map(|z| json!(format!("{:?}", z)))
.collect();
out.push(("zstyle".into(), Value::Array(arr)));
}
});
let mut env_map = serde_json::Map::new();
for (k, v) in std::env::vars() {
env_map.insert(k, Value::String(v));
}
if !env_map.is_empty() {
out.push(("env".into(), Value::Object(env_map)));
}
if let Ok(p) = std::env::var("PATH") {
let parts: Vec<Value> = p
.split(':')
.filter(|s| !s.is_empty())
.map(|s| Value::String(s.into()))
.collect();
if !parts.is_empty() {
out.push(("path".into(), Value::Array(parts)));
}
}
if let Ok(p) = std::env::var("MANPATH") {
let parts: Vec<Value> = p
.split(':')
.filter(|s| !s.is_empty())
.map(|s| Value::String(s.into()))
.collect();
if !parts.is_empty() {
out.push(("manpath".into(), Value::Array(parts)));
}
}
out
}
fn map_to_json<'a, I>(iter: I) -> Value
where
I: IntoIterator<Item = (&'a String, &'a String)>,
{
let map: serde_json::Map<String, Value> = iter
.into_iter()
.map(|(k, v)| (k.clone(), Value::String(v.clone())))
.collect();
Value::Object(map)
}
fn entries_to_json(entries: &[(String, String)]) -> Value {
let map: serde_json::Map<String, Value> = entries
.iter()
.map(|(k, v)| (k.clone(), Value::String(v.clone())))
.collect();
Value::Object(map)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn entries_to_json_empty_yields_empty_object() {
let v = entries_to_json(&[]);
assert!(v.is_object(), "result must be a JSON object");
assert_eq!(v.as_object().unwrap().len(), 0);
}
#[test]
fn entries_to_json_preserves_keys_and_string_values() {
let entries = vec![
("ll".to_string(), "ls -la".to_string()),
("gs".to_string(), "git status".to_string()),
("..".to_string(), "cd ..".to_string()),
];
let v = entries_to_json(&entries);
let obj = v.as_object().expect("object");
assert_eq!(obj.len(), 3);
assert_eq!(obj.get("ll").and_then(Value::as_str), Some("ls -la"));
assert_eq!(obj.get("gs").and_then(Value::as_str), Some("git status"));
assert_eq!(obj.get("..").and_then(Value::as_str), Some("cd .."));
}
#[test]
fn entries_to_json_all_values_are_string_type() {
let entries = vec![
("a".to_string(), "1".to_string()),
("b".to_string(), "true".to_string()),
("c".to_string(), "null".to_string()),
];
let v = entries_to_json(&entries);
for (_, val) in v.as_object().expect("object").iter() {
assert!(val.is_string(), "every value must serialize as string");
}
}
#[test]
fn entries_to_json_handles_duplicate_keys_last_wins() {
let entries = vec![
("k".to_string(), "first".to_string()),
("k".to_string(), "second".to_string()),
];
let v = entries_to_json(&entries);
let obj = v.as_object().expect("object");
assert_eq!(obj.len(), 1, "dup keys collapse to one slot");
assert_eq!(obj.get("k").and_then(Value::as_str), Some("second"));
}
#[test]
fn entries_to_json_empty_string_values_preserved() {
let entries = vec![("empty".to_string(), "".to_string())];
let v = entries_to_json(&entries);
assert_eq!(
v.as_object().unwrap().get("empty").and_then(Value::as_str),
Some("")
);
}
#[test]
fn entries_to_json_unicode_keys_and_values() {
let entries = vec![
("café".to_string(), "echo 茶".to_string()),
("→".to_string(), "echo ←".to_string()),
];
let v = entries_to_json(&entries);
let obj = v.as_object().expect("object");
assert_eq!(obj.get("café").and_then(Value::as_str), Some("echo 茶"));
assert_eq!(obj.get("→").and_then(Value::as_str), Some("echo ←"));
}
#[test]
fn map_to_json_empty_iterator_yields_empty_object() {
let empty: HashMap<String, String> = HashMap::new();
let v = map_to_json(empty.iter());
assert!(v.is_object());
assert_eq!(v.as_object().unwrap().len(), 0);
}
#[test]
fn map_to_json_round_trips_hashmap_entries() {
let mut m: HashMap<String, String> = HashMap::new();
m.insert("PATH".to_string(), "/usr/bin:/bin".to_string());
m.insert("HOME".to_string(), "/home/user".to_string());
m.insert("SHELL".to_string(), "/usr/bin/zsh".to_string());
let v = map_to_json(m.iter());
let obj = v.as_object().expect("object");
assert_eq!(obj.len(), 3);
assert_eq!(
obj.get("PATH").and_then(Value::as_str),
Some("/usr/bin:/bin")
);
assert_eq!(obj.get("HOME").and_then(Value::as_str), Some("/home/user"));
assert_eq!(
obj.get("SHELL").and_then(Value::as_str),
Some("/usr/bin/zsh")
);
}
#[test]
fn map_to_json_clones_owned_strings() {
let mut m: HashMap<String, String> = HashMap::new();
m.insert("k".to_string(), "v".to_string());
let v = map_to_json(m.iter());
drop(m);
assert_eq!(
v.as_object().unwrap().get("k").and_then(Value::as_str),
Some("v")
);
}
#[test]
fn map_to_json_values_serialize_as_strings_not_inferred_types() {
let mut m: HashMap<String, String> = HashMap::new();
m.insert("INT_LIKE".to_string(), "42".to_string());
m.insert("BOOL_LIKE".to_string(), "true".to_string());
m.insert("NULL_LIKE".to_string(), "null".to_string());
let v = map_to_json(m.iter());
for (_, val) in v.as_object().expect("object").iter() {
assert!(
val.is_string(),
"shell values are always strings on the wire"
);
}
}
#[test]
fn map_to_json_serialized_form_is_valid_json() {
let mut m: HashMap<String, String> = HashMap::new();
m.insert("a".to_string(), "1".to_string());
m.insert("b".to_string(), "two".to_string());
let v = map_to_json(m.iter());
let s = serde_json::to_string(&v).expect("serialize");
let back: Value = serde_json::from_str(&s).expect("deserialize");
assert_eq!(back, v);
}
#[test]
fn entries_to_json_serialized_form_is_valid_json() {
let entries = vec![
("alpha".to_string(), "first".to_string()),
("omega".to_string(), "last".to_string()),
];
let v = entries_to_json(&entries);
let s = serde_json::to_string(&v).expect("serialize");
let back: Value = serde_json::from_str(&s).expect("deserialize");
assert_eq!(back, v);
}
}