use std::path::PathBuf; /// Adds .cmdlink/bins to the user's PATH environment variable. fn add_to_user_path(new_path: &str) -> Result<(), Box> { #[cfg(target_os = "windows")] add_win_path(new_path)?; #[cfg(target_os = "macos")] add_macos_path(new_path)?; Ok(()) } /// Setup the project directory fn setup_project_dir() -> Result> { let base_path = dirs::home_dir().expect("home directory not found!").join(".cmdlink"); // Create the project directory if it doesn't exist std::fs::create_dir_all(&base_path).map_err(|e| format!("error creating project directory: {e}"))?; // Create the bins directory within the project directory let bins_dir = base_path.join("bins"); std::fs::create_dir_all(&bins_dir).map_err(|e| format!("error creating bins directory: {e}"))?; add_to_user_path(&bins_dir.display().to_string())?; Ok(base_path) } fn main() { // Setup the project directory if let Err(err) = setup_project_dir() { panic!("error setting up project directory: {}", err); } } #[cfg(target_os = "windows")] /// Adds a new path to the user's PATH environment variable on Windows. fn add_win_path(new_path: &str) -> Result<(), Box> { use std::{ffi::OsString, os::windows::ffi::OsStrExt}; use base64::{engine::general_purpose, Engine}; use windows_registry::CURRENT_USER; let environment_key = CURRENT_USER.open("Environment")?; // Get the current PATH value let current_path = environment_key.get_string("Path").unwrap(); // Check if the new path is already in the PATH to avoid duplicates if current_path.split(';').any(|p| p == new_path) { return Ok(()); } let updated_path = if current_path.is_empty() { new_path.to_string() } else { format!("{}{}", current_path, new_path) }; let ps_command = format!("[Environment]::SetEnvironmentVariable('PATH', '{}', 'User')", updated_path); // Encode the powershell command as UTF-16LE base64 and run let utf_16_bytes: Vec = OsString::from(ps_command) .encode_wide() .flat_map(|u| u.to_le_bytes()) .collect(); runas::Command::new("powershell.exe") .args(&[ "-ExecutionPolicy", "Bypass", "-NoProfile", "-EncodedCommand", &general_purpose::STANDARD.encode(utf_16_bytes), ]) .gui(true) .status()?; Ok(()) } #[cfg(target_family = "unix")] /// Adds a new path to /etc/paths on MacOs. fn add_macos_path(new_path: &str) -> Result<(), Box> { use std::{ fs::{self, File}, io::Write, }; let profile_file = dirs::home_dir().expect("home directory not found!").join(".profile"); // If .profile file doesn't exist, create it. let mut file = if !profile_file.exists() { File::create_new(&profile_file)? } else { File::options().append(true).create(true).open(&profile_file)? }; // Read the profile file. If the new path is not already in the PATH, add it. let profile = fs::read_to_string(&profile_file)?; if !profile.lines().any(|l| l.contains(new_path)) { file.write_all(format!("export PATH=$PATH:{}\n", new_path).as_bytes())?; } Ok(()) }