use once_cell::sync::Lazy;
use std::sync::RwLock;
use crate::builtins::common::env as runtime_env;
pub const PATH_LIST_SEPARATOR: char = if cfg!(windows) { ';' } else { ':' };
#[derive(Debug, Clone)]
struct PathState {
current: String,
}
impl PathState {
fn initialise() -> Self {
Self {
current: initial_path_string(),
}
}
}
fn initial_path_string() -> String {
let mut parts = Vec::<String>::new();
for var in ["RUNMAT_PATH", "MATLABPATH"] {
if let Ok(value) = runtime_env::var(var) {
parts.extend(
value
.split(PATH_LIST_SEPARATOR)
.map(|part| part.trim())
.filter(|part| !part.is_empty())
.map(|part| part.to_string()),
);
}
}
join_parts(&parts)
}
fn join_parts(parts: &[String]) -> String {
let mut joined = String::new();
for (idx, part) in parts.iter().enumerate() {
if idx > 0 {
joined.push(PATH_LIST_SEPARATOR);
}
joined.push_str(part);
}
joined
}
static PATH_STATE: Lazy<RwLock<PathState>> = Lazy::new(|| RwLock::new(PathState::initialise()));
pub fn current_path_string() -> String {
PATH_STATE
.read()
.map(|guard| guard.current.clone())
.unwrap_or_else(|poison| poison.into_inner().current.clone())
}
pub fn append_to_path(segments: &[String]) {
if segments.is_empty() {
return;
}
let mut guard = PATH_STATE
.write()
.unwrap_or_else(|poison| poison.into_inner());
let mut parts = split_segments(&guard.current);
parts.extend(segments.iter().cloned());
guard.current = join_parts(&parts);
}
pub fn set_path_string(new_path: &str) {
if new_path.is_empty() {
runtime_env::remove_var("RUNMAT_PATH");
} else {
runtime_env::set_var("RUNMAT_PATH", new_path);
}
let mut guard = PATH_STATE
.write()
.unwrap_or_else(|poison| poison.into_inner());
guard.current = new_path.to_string();
}
pub fn current_path_segments() -> Vec<String> {
let path = current_path_string();
split_segments(&path)
}
fn split_segments(path: &str) -> Vec<String> {
path.split(PATH_LIST_SEPARATOR)
.map(|part| part.trim())
.filter(|part| !part.is_empty())
.map(|part| part.to_string())
.collect()
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[test]
fn join_and_split_round_trip() {
let parts = vec!["/tmp/a".to_string(), "/tmp/b".to_string()];
let joined = join_parts(&parts);
assert_eq!(split_segments(&joined), parts);
}
}