use std::{fs, path::Path};
use crate::pm::core::types::{InstallMethod, Origin};
pub fn detect(path: &Path) -> InstallMethod {
let canonical_path = match fs::canonicalize(path) {
Ok(p) => p,
Err(_) => return InstallMethod::Unknown,
};
let path_str = canonical_path.to_string_lossy();
if let Some(method) = check_environment_patterns(&path_str) {
return method;
}
let home = std::env::var("HOME").unwrap_or_default();
if let Some(method) = check_toolchain_patterns(&path_str, &home) {
return method;
}
check_system_patterns(&path_str)
}
fn check_environment_patterns(path_str: &str) -> Option<InstallMethod> {
if let Ok(homebrew_prefix) = std::env::var("HOMEBREW_PREFIX")
&& path_str.starts_with(&homebrew_prefix)
{
if path_str.contains("/corepack/dist/") {
return Some(InstallMethod::Chain(vec![
Origin::PackageManager("Homebrew"),
Origin::Wrapper("Corepack"),
]));
}
return Some(InstallMethod::Chain(vec![Origin::PackageManager(
"Homebrew",
)]));
}
None
}
fn check_toolchain_patterns(path_str: &str, home: &str) -> Option<InstallMethod> {
if path_str.contains(&format!("{}/.cargo/", home))
|| path_str.contains(&format!("{}/.rustup/", home))
{
return Some(InstallMethod::Chain(vec![Origin::Toolchain("Rustup")]));
}
if path_str.contains(&format!("{}/.nvm/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("NVM")]));
}
if path_str.contains(&format!("{}/.fnm/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("fnm")]));
}
if path_str.contains(&format!("{}/.n/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("n")]));
}
if path_str.contains(&format!("{}/.nodenv/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("nodenv")]));
}
if path_str.contains(&format!("{}/.ghcup/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("GHCup")]));
}
if path_str.contains(&format!("{}/.rvm/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("RVM")]));
}
if path_str.contains(&format!("{}/.rbenv/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("rbenv")]));
}
if path_str.contains(&format!("{}/.pyenv/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("pyenv")]));
}
if path_str.contains(&format!("{}/.g/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("g")]));
}
if path_str.contains(&format!("{}/.gvm/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("gvm")]));
}
if path_str.contains(&format!("{}/.phpbrew/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("phpbrew")]));
}
if path_str.contains(&format!("{}/.opam/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("opam")]));
}
if path_str.contains(&format!("{}/.perlbrew/", home))
|| path_str.contains(&format!("{}/perl5/perlbrew/", home))
{
return Some(InstallMethod::Chain(vec![Origin::Toolchain("perlbrew")]));
}
if path_str.contains(&format!("{}/.kiex/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("kiex")]));
}
if path_str.contains(&format!("{}/.exenv/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("exenv")]));
}
if path_str.contains(&format!("{}/.luaver/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Toolchain("luaver")]));
}
if path_str.contains(&format!("{}/.asdf/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Wrapper("asdf")]));
}
if path_str.contains(&format!("{}/.volta/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Wrapper("Volta")]));
}
if path_str.contains(&format!("{}/.local/share/mise/", home))
|| path_str.contains(&format!("{}/.config/mise/", home))
{
return Some(InstallMethod::Chain(vec![Origin::Wrapper("mise")]));
}
if path_str.contains(&format!("{}/.bun/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Direct(Some("Bun"))]));
}
if path_str.contains(&format!("{}/.deno/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Direct(Some("Deno"))]));
}
if path_str.contains(&format!("{}/.conda/", home))
|| path_str.contains(&format!("{}/anaconda3/", home))
|| path_str.contains(&format!("{}/miniconda3/", home))
|| path_str.contains(&format!("{}/mambaforge/", home))
|| path_str.contains(&format!("{}/miniforge/", home))
|| path_str.contains("/opt/conda/")
|| path_str.contains("/opt/anaconda/")
|| path_str.contains("/opt/miniconda/")
{
return Some(InstallMethod::Chain(vec![Origin::PackageManager("Conda")]));
}
if path_str.contains(&format!("{}/.local/bin/", home)) {
if path_str.contains("poetry") {
return Some(InstallMethod::Chain(vec![Origin::Direct(Some("Poetry"))]));
}
return Some(InstallMethod::Chain(vec![Origin::Direct(Some("pipx/pip"))]));
}
if path_str.contains(&format!("{}/.local/pipx/venvs/", home)) {
return Some(InstallMethod::Chain(vec![Origin::PackageManager("Pipx")]));
}
if path_str.contains(&format!("{}/.poetry/", home)) {
return Some(InstallMethod::Chain(vec![Origin::Direct(Some("Poetry"))]));
}
None
}
fn check_system_patterns(path_str: &str) -> InstallMethod {
if path_str.contains("/nix/store/") {
return InstallMethod::Chain(vec![Origin::PackageManager("Nix")]);
}
if path_str.starts_with("/opt/local/") {
if std::path::Path::new("/opt/local/etc/macports").exists()
|| std::path::Path::new("/opt/local/var/macports").exists()
|| std::path::Path::new("/opt/local/libexec/macports").exists()
{
return InstallMethod::Chain(vec![Origin::PackageManager("MacPorts")]);
}
return InstallMethod::Chain(vec![Origin::Direct(Some("/opt/local"))]);
}
if path_str.starts_with("/snap/") || path_str.contains("/var/lib/snapd/") {
return InstallMethod::Chain(vec![Origin::PackageManager("Snap")]);
}
if path_str.contains("/var/lib/flatpak/") || path_str.contains("/.var/app/") {
return InstallMethod::Chain(vec![Origin::PackageManager("Flatpak")]);
}
if path_str.contains("\\scoop\\apps\\") || path_str.contains("/scoop/apps/") {
return InstallMethod::Chain(vec![Origin::PackageManager("Scoop")]);
}
if path_str.contains("\\ProgramData\\chocolatey\\")
|| path_str.contains("/ProgramData/chocolatey/")
{
return InstallMethod::Chain(vec![Origin::PackageManager("Chocolatey")]);
}
if path_str.starts_with("/System/")
|| path_str.starts_with("/usr/bin/")
|| path_str.starts_with("/bin/")
|| path_str.starts_with("/sbin/")
|| path_str.starts_with("/usr/sbin/")
{
return InstallMethod::System;
}
if path_str.starts_with("/usr/local/") {
if path_str.contains("/corepack/dist/") {
return InstallMethod::Chain(vec![
Origin::Toolchain("Node.js"),
Origin::Wrapper("Corepack"),
]);
}
return InstallMethod::Chain(vec![Origin::Direct(Some("Direct Install"))]);
}
InstallMethod::Unknown
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_with_nonexistent_path() {
let nonexistent_path = std::path::PathBuf::from("/this/path/does/not/exist");
let result = detect(&nonexistent_path);
assert_eq!(result, InstallMethod::Unknown);
}
}