1#![forbid(warnings)]
4#![warn(
5 missing_copy_implementations,
6 trivial_casts,
7 trivial_numeric_casts,
8 unsafe_code,
9 unused_extern_crates,
10 unused_import_braces,
11 unused_qualifications,
12 unused_results,
13 variant_size_differences
14)]
15
16#[cfg(windows)]
17extern crate winapi;
18#[cfg(windows)]
19extern crate winreg;
20
21use std::ffi::OsStr;
22use std::path::PathBuf;
23use std::process::{Command, Stdio};
24use std::str;
25
26#[cfg(windows)]
27const LOCATE_COMMAND: &'static str = "where";
28#[cfg(not(windows))]
29const LOCATE_COMMAND: &str = "which";
30
31fn git_ran_ok<S: AsRef<OsStr>>(binary_path: S) -> bool {
32 Command::new(binary_path)
33 .arg("--version")
34 .stdout(Stdio::null())
35 .stderr(Stdio::null())
36 .status()
37 .is_ok()
38}
39
40fn try_git_with_no_path() -> Option<::std::path::PathBuf> {
41 if let Ok(output) = Command::new(LOCATE_COMMAND).arg("git").output() {
42 let git = str::from_utf8(&output.stdout)
43 .unwrap_or_else(|_| panic!("Non-UTF8 output when running `{} git`.", LOCATE_COMMAND))
44 .trim()
45 .lines()
46 .next()
47 .unwrap_or_else(|| {
48 panic!(
49 "Should have had at least one line of text when running `{} git`.",
50 LOCATE_COMMAND
51 )
52 });
53 if git_ran_ok(git) {
54 return Some(PathBuf::from(git));
55 }
56 }
57 None
58}
59
60#[cfg(windows)]
61mod find_git {
62 use super::{git_ran_ok, try_git_with_no_path};
63 use std::ffi::OsStr;
64 use std::io::Result;
65 use std::path::PathBuf;
66 use winapi::shared::minwindef::HKEY;
67 use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ};
68 use winreg::RegKey;
69
70 fn try_full_path_to_git<P: AsRef<OsStr>>(
71 predefined_key: HKEY,
72 subkey_path: P,
73 value: P,
74 ) -> Option<PathBuf> {
75 let root = RegKey::predef(predefined_key);
76 if let Ok(subkey) = root.open_subkey_with_flags(subkey_path, KEY_READ) {
77 let subkey_value: Result<String> = subkey.get_value(value);
78 if let Ok(install_path) = subkey_value {
79 let binary_path = PathBuf::from(&install_path).join("bin").join("git.exe");
80 if git_ran_ok(binary_path.as_os_str()) {
81 return Some(binary_path);
82 }
83 }
84 }
85 None
86 }
87
88 #[rustfmt::skip]
91 pub fn git_path() -> Option<PathBuf> {
92 const SUBKEY_RECENT: &'static str = "Software\\GitForWindows";
93 const SUBKEY_32_BIT: &'static str =
94 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1";
95 const SUBKEY_64_BIT: &'static str =
96 "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1";
97 try_git_with_no_path()
98 .or_else(|| try_full_path_to_git(HKEY_LOCAL_MACHINE, SUBKEY_RECENT, "InstallPath"))
99 .or_else(|| try_full_path_to_git(HKEY_CURRENT_USER, SUBKEY_32_BIT, "InstallLocation"))
100 .or_else(|| try_full_path_to_git(HKEY_CURRENT_USER, SUBKEY_64_BIT, "InstallLocation"))
101 .or_else(|| try_full_path_to_git(HKEY_LOCAL_MACHINE, SUBKEY_32_BIT, "InstallLocation"))
102 .or_else(|| try_full_path_to_git(HKEY_LOCAL_MACHINE, SUBKEY_64_BIT, "InstallLocation"))
103 }
104}
105
106#[cfg(not(windows))]
107mod find_git {
108 pub fn git_path() -> Option<::std::path::PathBuf> {
109 super::try_git_with_no_path()
110 }
111}
112
113pub use find_git::git_path;