Support Termux (#10013)

This commit is contained in:
Skyler Hawthorne 2023-08-28 02:53:25 -04:00 committed by GitHub
parent 487f1a97ea
commit 38f454d5ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 52 deletions

View File

@ -16,5 +16,5 @@ dirs-next = "2.0"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
omnipath = "0.1" omnipath = "0.1"
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] [target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))'.dependencies]
pwd = "1.3" pwd = "1.3"

View File

@ -1,7 +1,22 @@
#[cfg(all(unix, not(target_os = "macos")))] #[cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))]
use pwd::Passwd; use pwd::Passwd;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[cfg(target_os = "macos")]
const FALLBACK_USER_HOME_BASE_DIR: &str = "/Users";
#[cfg(target_os = "windows")]
const FALLBACK_USER_HOME_BASE_DIR: &str = "C:\\Users\\";
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))]
const FALLBACK_USER_HOME_BASE_DIR: &str = "/home";
#[cfg(all(unix, target_os = "android"))]
const FALLBACK_USER_HOME_BASE_DIR: &str = "/data";
#[cfg(target_os = "android")]
const TERMUX_HOME: &str = "/data/data/com.termux/files/home";
fn expand_tilde_with_home(path: impl AsRef<Path>, home: Option<PathBuf>) -> PathBuf { fn expand_tilde_with_home(path: impl AsRef<Path>, home: Option<PathBuf>) -> PathBuf {
let path = path.as_ref(); let path = path.as_ref();
@ -45,77 +60,61 @@ fn expand_tilde_with_home(path: impl AsRef<Path>, home: Option<PathBuf>) -> Path
} }
} }
#[cfg(all(unix, not(target_os = "macos")))] fn fallback_home_dir(username: &str) -> PathBuf {
PathBuf::from_iter([FALLBACK_USER_HOME_BASE_DIR, username])
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))]
fn user_home_dir(username: &str) -> PathBuf { fn user_home_dir(username: &str) -> PathBuf {
let passwd = Passwd::from_name(username); let passwd = Passwd::from_name(username);
match &passwd.ok() { match &passwd.ok() {
Some(Some(dir)) => PathBuf::from(&dir.dir), Some(Some(dir)) => PathBuf::from(&dir.dir),
_ => { _ => fallback_home_dir(username),
let mut file = String::from("/home/");
file.push_str(username);
PathBuf::from(file)
}
} }
// PathBuf::from(concat!("/home/", username)),
// Returns home dir of user.
} }
#[cfg(target_os = "macos")] #[cfg(any(target_os = "android", target_os = "windows", target_os = "macos"))]
fn user_home_dir(username: &str) -> PathBuf { fn user_home_dir(username: &str) -> PathBuf {
use std::path::Component;
match dirs_next::home_dir() { match dirs_next::home_dir() {
None => { None => {
let mut expected_path = String::from("/Users/"); // Termux always has the same home directory
expected_path.push_str(username); #[cfg(target_os = "android")]
let path = Path::new(&expected_path); if is_termux() {
let mut home = PathBuf::new(); return PathBuf::from(TERMUX_HOME);
home.push(path); }
home
fallback_home_dir(username)
} }
Some(user) => { Some(user) => {
let mut expected_path = user; let mut expected_path = user;
expected_path.pop();
expected_path.push(Path::new(username)); if !cfg!(target_os = "android")
&& expected_path
.components()
.last()
.map(|last| last != Component::Normal(username.as_ref()))
.unwrap_or(false)
{
expected_path.pop();
expected_path.push(Path::new(username));
}
if expected_path.is_dir() { if expected_path.is_dir() {
expected_path expected_path
} else { } else {
let mut expected_path_as_string = String::from("/Users/"); fallback_home_dir(username)
expected_path_as_string.push_str(username);
let path = Path::new(&expected_path_as_string);
let mut home = PathBuf::new();
home.push(path);
home
} }
} }
} }
} }
#[cfg(target_os = "windows")] /// Returns true if the shell is running inside the Termux terminal emulator
fn user_home_dir(username: &str) -> PathBuf { /// app.
match dirs_next::home_dir() { #[cfg(target_os = "android")]
None => { fn is_termux() -> bool {
let mut expected_path = String::from("C:\\Users\\"); std::env::var("TERMUX_VERSION").is_ok()
expected_path.push_str(username);
let path = Path::new(&expected_path);
let mut home = PathBuf::new();
home.push(path);
home
}
Some(user) => {
let mut expected_path = user;
expected_path.pop();
expected_path.push(Path::new(username));
if expected_path.is_dir() {
expected_path
} else {
let mut expected_path_as_string = String::from("C:\\Users\\");
expected_path_as_string.push_str(username);
let path = Path::new(&expected_path_as_string);
let mut home = PathBuf::new();
home.push(path);
home
}
}
}
} }
fn expand_tilde_with_another_user_home(path: &Path) -> PathBuf { fn expand_tilde_with_another_user_home(path: &Path) -> PathBuf {
@ -211,4 +210,22 @@ mod tests {
fn string_with_double_tilde_backslash() { fn string_with_double_tilde_backslash() {
check_expanded("~\\\\test\\test2/test3"); check_expanded("~\\\\test\\test2/test3");
} }
// [TODO] Figure out how to reliably test with real users.
#[test]
fn user_home_dir_fallback() {
let user = "nonexistent";
let expected_home = PathBuf::from_iter([FALLBACK_USER_HOME_BASE_DIR, user]);
#[cfg(target_os = "android")]
let expected_home = if is_termux() {
PathBuf::from(TERMUX_HOME)
} else {
expected_home
};
let actual_home = super::user_home_dir(user);
assert_eq!(expected_home, actual_home, "wrong home");
}
} }