onpath 0.2.0

Get your tools on the PATH — cross-shell, cross-platform, zero fuss
Documentation
use std::path::{Path, PathBuf};

use crate::config::Position;
use crate::context::SystemContext;
use crate::shell::{Shell, ShellKind};

pub struct Tcsh;

impl Shell for Tcsh {
    fn kind(&self) -> ShellKind {
        ShellKind::Tcsh
    }

    fn env_extension(&self) -> &'static str {
        ".tcsh"
    }

    fn env_script(&self, dir: &Path, position: Position) -> String {
        let dir = dir.display();
        let setenv = match position {
            Position::Prepend => format!("setenv PATH \"{dir}:${{PATH}}\""),
            Position::Append => format!("setenv PATH \"${{PATH}}:{dir}\""),
        };
        format!(
            r#"#!/bin/tcsh
# Generated by onpath. Do not edit.
switch (":${{PATH}}:")
    case *\:"{dir}"\:*:
        breaksw
    default:
        {setenv}
        breaksw
endsw
"#
        )
    }

    fn source_line(&self, env_script_path: &Path) -> String {
        format!("source \"{}\"", env_script_path.display())
    }

    fn rc_candidates(&self, ctx: &SystemContext) -> Vec<PathBuf> {
        vec![
            ctx.home_dir().join(".tcshrc"),
            ctx.home_dir().join(".cshrc"),
        ]
    }

    fn primary_rc(&self, ctx: &SystemContext) -> PathBuf {
        ctx.home_dir().join(".tcshrc")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn env_script_uses_switch_pattern() {
        let script = Tcsh.env_script(Path::new("/app/bin"), Position::Prepend);
        assert!(script.contains("switch"));
        assert!(script.contains("setenv PATH"));
        assert!(script.contains("endsw"));
    }

    #[test]
    fn env_script_append() {
        let script = Tcsh.env_script(Path::new("/app/bin"), Position::Append);
        assert!(script.contains("\"${PATH}:/app/bin\""));
    }

    #[test]
    fn rc_candidates_includes_tcshrc_and_cshrc() {
        let ctx = SystemContext::with_home(PathBuf::from("/home/user"));
        let candidates = Tcsh.rc_candidates(&ctx);
        assert!(candidates.contains(&PathBuf::from("/home/user/.tcshrc")));
        assert!(candidates.contains(&PathBuf::from("/home/user/.cshrc")));
    }
}