1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use directories::{ProjectDirs, UserDirs};
use regex::Regex;
use std::path::{Path, PathBuf};

/// uses the PATH environment variable to search
/// for a filename matching the specified name.
/// if a matching filename is not found, it
/// will check for the existence of name.exe
/// and name.bat
pub fn which(name: &str) -> Option<PathBuf> {
    let extensions = vec!["", ".exe", ".bat"];
    for ext in extensions.iter() {
        let exe_name = format!("{}{}", name, ext);
        match which_exact(&exe_name) {
            Some(path) => {
                return Some(path);
            }
            None => {}
        }
    }
    None
}

pub fn shebang(path: &Path) -> super::result::Result<String> {
    match std::fs::read_to_string(path) {
        Ok(text) => match Regex::new(r"#!\s*([\w]+)") {
            Ok(re) => match re.captures(&text) {
                Some(caps) => {
                    if caps.len() > 1 {
                        Ok(caps[1].to_string())
                    } else {
                        Ok("".to_string())
                    }
                }
                None => Ok("".to_string()),
            },
            Err(e) => Err(super::error::Error::from(e)),
        },
        Err(e) => Err(super::error::Error::from(e)),
    }
}

fn which_exact(name: &str) -> Option<PathBuf> {
    std::env::var_os("PATH").and_then(|paths| {
        std::env::split_paths(&paths)
            .filter_map(|dir| {
                let full_path = dir.join(&name);
                if full_path.is_file() {
                    Some(full_path)
                } else {
                    None
                }
            })
            .next()
    })
}

pub fn home() -> PathBuf {
    match UserDirs::new() {
        Some(user_dirs) => user_dirs.home_dir().to_path_buf(),
        None => std::env::temp_dir(),
    }
}

pub fn env_or_home(env_name: &str) -> PathBuf {
    match std::env::var(env_name) {
        Ok(val) => {
            let path = PathBuf::from(val);
            if path.exists() {
                path
            } else {
                home()
            }
        }
        Err(_) => home(),
    }
}

pub fn project_path<P: AsRef<Path>>(
    qualifier: &str,
    organization: &str,
    application: &str,
    rel_path: P,
) -> PathBuf {
    match ProjectDirs::from(qualifier, organization, application) {
        Some(project_dirs) => {
            let dir = project_dirs.data_dir();
            dir.to_path_buf().join(rel_path)
        }
        None => home()
            .join(qualifier)
            .join(organization)
            .join(application)
            .join(rel_path),
    }
}

#[cfg(test)]
#[test]
fn shebang_test() {
    match which("rake") {
        Some(rake) => {
            let text = std::fs::read_to_string(&rake).unwrap();
            assert!(text.contains("#! ruby"));
            match shebang(&rake) {
                Ok(shebang) => {
                    assert_eq!("ruby", shebang, "rake text: {}, shebang: {}", text, shebang)
                }
                Err(e) => assert!(false, "did not find shebang for {:?}, {:?}", rake, e),
            }
        }
        None => {}
    }
}

#[test]
fn home_test() {
    assert!(home().exists(), "home() '{:?}' does not exist", home());
}