shellcomp 0.1.13

Shell completion installation and activation helpers for Rust CLI tools
Documentation
use std::path::Path;

use crate::error::Result;
use crate::infra::fs;
use crate::model::{ActivationMode, ActivationReport, Availability, CleanupReport, FileChange};
use crate::shell::{ActivationOutcome, CleanupOutcome};

pub(crate) fn install(_program_name: &str, target_path: &Path) -> Result<ActivationOutcome> {
    Ok(ActivationOutcome {
        report: ActivationReport {
            mode: ActivationMode::NativeDirectory,
            availability: Availability::ActiveNow,
            location: Some(target_path.to_path_buf()),
            reason: Some("Installed into Fish's native completions directory.".to_owned()),
            next_step: None,
        },
        affected_locations: Vec::new(),
    })
}

pub(crate) fn uninstall(_program_name: &str, _target_path: &Path) -> Result<CleanupOutcome> {
    Ok(CleanupOutcome {
        cleanup: CleanupReport {
            mode: ActivationMode::NativeDirectory,
            change: FileChange::Absent,
            location: None,
            reason: Some(
                "Fish does not use a managed shell profile block for completion activation."
                    .to_owned(),
            ),
            next_step: None,
        },
        affected_locations: Vec::new(),
    })
}

pub(crate) fn detect(_program_name: &str, target_path: &Path) -> Result<ActivationReport> {
    let installed = fs::file_exists(target_path);

    Ok(ActivationReport {
        mode: ActivationMode::NativeDirectory,
        availability: if installed {
            Availability::ActiveNow
        } else {
            Availability::ManualActionRequired
        },
        location: Some(target_path.to_path_buf()),
        reason: Some(if installed {
            "Completion file is installed in Fish's native completions directory.".to_owned()
        } else {
            format!(
                "Completion file `{}` is not installed.",
                target_path.display()
            )
        }),
        next_step: if installed {
            None
        } else {
            Some(
                "Run your CLI's completion install command or place the completion file into Fish's completions directory."
                    .to_owned(),
            )
        },
    })
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::path::Path;

    use super::{detect, install, uninstall};
    use crate::model::{ActivationMode, Availability, FileChange};

    #[test]
    fn install_reports_native_directory_activation() {
        let report = install("tool", Path::new("/tmp/fish/completions/tool.fish"))
            .expect("install should succeed");

        assert_eq!(report.report.mode, ActivationMode::NativeDirectory);
        assert_eq!(report.report.availability, Availability::ActiveNow);
        assert!(report.report.next_step.is_none());
    }

    #[test]
    fn detect_reports_active_now_when_completion_exists() {
        let temp_root = crate::tests::temp_dir("fish-detect-installed");
        let target = temp_root.join("tool.fish");
        fs::write(&target, "complete -c tool\n").expect("completion file should be writable");

        let report = detect("tool", &target).expect("detect should succeed");

        assert_eq!(report.mode, ActivationMode::NativeDirectory);
        assert_eq!(report.availability, Availability::ActiveNow);
        assert_eq!(report.location, Some(target));
        assert!(report.next_step.is_none());
    }

    #[test]
    fn uninstall_reports_no_profile_cleanup() {
        let report = uninstall("tool", Path::new("/tmp/fish/completions/tool.fish"))
            .expect("uninstall should succeed");

        assert_eq!(report.cleanup.mode, ActivationMode::NativeDirectory);
        assert_eq!(report.cleanup.change, FileChange::Absent);
        assert!(report.cleanup.location.is_none());
    }
}