use crate::shell::{Shell, ShellType};
use std::path::{Path, PathBuf};
use yansi::Paint;
#[derive(Debug, Clone)]
pub struct ModifiedFile {
pub file_type: FileType,
pub path: PathBuf,
pub action: FileAction,
}
#[derive(Debug, Clone)]
pub enum FileType {
RcFile,
EnvironmentFile,
RegistryEntry,
}
#[derive(Debug, Clone)]
pub enum FileAction {
Created,
Modified,
SourceAdded,
}
#[derive(Debug, Clone)]
pub struct PostSetupInstructions {
pub shell_type: ShellType,
pub modified_files: Vec<ModifiedFile>,
pub primary_source_command: String,
pub alternative_instructions: Vec<String>,
}
impl PostSetupInstructions {
pub fn generate_for_shell(shell: &Shell, modified_files: Vec<ModifiedFile>) -> Self {
let primary_source_command = Self::get_primary_source_command(shell, &modified_files);
let alternative_instructions = Self::generate_alternatives(shell, &modified_files);
Self {
shell_type: shell.shell_type,
modified_files,
primary_source_command,
alternative_instructions,
}
}
fn get_primary_source_command(shell: &Shell, modified_files: &[ModifiedFile]) -> String {
for file in modified_files {
match file.file_type {
FileType::RcFile => {
return Self::format_source_command(shell, &file.path);
}
_ => continue,
}
}
for file in modified_files {
match file.file_type {
FileType::EnvironmentFile => {
return Self::format_source_command(shell, &file.path);
}
_ => continue,
}
}
"Restart your shell to apply changes".to_string()
}
fn format_source_command(shell: &Shell, file_path: &Path) -> String {
match shell.shell_type {
ShellType::PowerShell => {
format!(". \"{}\"", file_path.display())
}
ShellType::Fish => {
format!("source \"{}\"", file_path.display())
}
_ => {
format!("source \"{}\"", file_path.display())
}
}
}
fn generate_alternatives(shell: &Shell, modified_files: &[ModifiedFile]) -> Vec<String> {
let mut alternatives = Vec::new();
alternatives.push("Restart your terminal".to_string());
for file in modified_files {
match file.file_type {
FileType::RcFile => {
continue;
}
FileType::EnvironmentFile => {
let source_cmd = Self::format_source_command(shell, &file.path);
if !alternatives.iter().any(|alt| alt.contains(&source_cmd)) {
alternatives.push(source_cmd);
}
}
FileType::RegistryEntry => {
if shell.is_windows_shell() && !shell.is_powershell_in_unix() {
alternatives
.push("Run 'refreshenv' to reload environment variables".to_string());
}
}
}
}
alternatives
}
pub fn display(&self) {
println!();
println!("{}", Paint::yellow("→ Next steps:"));
println!("• {}", self.primary_source_command);
println!("• Run 'zv --version' to verify the setup");
if self.alternative_instructions.len() > 1 {
println!();
println!("{}", Paint::dim("Alternative options:"));
for (i, instruction) in self.alternative_instructions.iter().enumerate() {
if i == 0 {
continue; }
println!(" • {}", Paint::dim(instruction));
}
}
}
}
pub fn create_rc_file_entry(path: PathBuf, action: FileAction) -> ModifiedFile {
ModifiedFile {
file_type: FileType::RcFile,
path,
action,
}
}
pub fn create_env_file_entry(path: PathBuf, action: FileAction) -> ModifiedFile {
ModifiedFile {
file_type: FileType::EnvironmentFile,
path,
action,
}
}
pub fn create_registry_entry() -> ModifiedFile {
ModifiedFile {
file_type: FileType::RegistryEntry,
path: PathBuf::from("HKEY_CURRENT_USER\\Environment\\PATH"),
action: FileAction::Modified,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shell::{OsFlavor, ShellContext};
fn create_test_shell(shell_type: ShellType) -> Shell {
Shell {
shell_type,
context: ShellContext {
target_os: OsFlavor::Unix,
is_wsl: false,
is_emulated: false,
},
}
}
#[test]
fn test_post_setup_instructions_bash_rc_file() {
let shell = create_test_shell(ShellType::Bash);
let modified_files = vec![create_rc_file_entry(
PathBuf::from("/home/user/.bashrc"),
FileAction::SourceAdded,
)];
let instructions = PostSetupInstructions::generate_for_shell(&shell, modified_files);
assert_eq!(
instructions.primary_source_command,
"source \"/home/user/.bashrc\""
);
assert_eq!(instructions.shell_type, ShellType::Bash);
}
#[test]
fn test_post_setup_instructions_zsh_env_file() {
let shell = create_test_shell(ShellType::Zsh);
let modified_files = vec![create_env_file_entry(
PathBuf::from("/home/user/.zv/env"),
FileAction::Created,
)];
let instructions = PostSetupInstructions::generate_for_shell(&shell, modified_files);
assert_eq!(
instructions.primary_source_command,
"source \"/home/user/.zv/env\""
);
assert_eq!(instructions.shell_type, ShellType::Zsh);
}
#[test]
fn test_post_setup_instructions_fish() {
let shell = create_test_shell(ShellType::Fish);
let modified_files = vec![create_rc_file_entry(
PathBuf::from("/home/user/.config/fish/config.fish"),
FileAction::SourceAdded,
)];
let instructions = PostSetupInstructions::generate_for_shell(&shell, modified_files);
assert_eq!(
instructions.primary_source_command,
"source \"/home/user/.config/fish/config.fish\""
);
assert_eq!(instructions.shell_type, ShellType::Fish);
}
#[test]
fn test_post_setup_instructions_powershell() {
let shell = create_test_shell(ShellType::PowerShell);
let modified_files = vec![create_env_file_entry(
PathBuf::from("/home/user/.zv/env.ps1"),
FileAction::Created,
)];
let instructions = PostSetupInstructions::generate_for_shell(&shell, modified_files);
assert_eq!(
instructions.primary_source_command,
". \"/home/user/.zv/env.ps1\""
);
assert_eq!(instructions.shell_type, ShellType::PowerShell);
}
#[test]
fn test_post_setup_instructions_priority_rc_over_env() {
let shell = create_test_shell(ShellType::Bash);
let modified_files = vec![
create_env_file_entry(PathBuf::from("/home/user/.zv/env"), FileAction::Created),
create_rc_file_entry(PathBuf::from("/home/user/.bashrc"), FileAction::SourceAdded),
];
let instructions = PostSetupInstructions::generate_for_shell(&shell, modified_files);
assert_eq!(
instructions.primary_source_command,
"source \"/home/user/.bashrc\""
);
}
#[test]
fn test_post_setup_instructions_no_files() {
let shell = create_test_shell(ShellType::Bash);
let modified_files = vec![];
let instructions = PostSetupInstructions::generate_for_shell(&shell, modified_files);
assert_eq!(
instructions.primary_source_command,
"Restart your shell to apply changes"
);
}
}