diff --git a/.gitignore b/.gitignore index aa085cd..010758b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ Cargo.lock /target **/*.rs.bk +.idea diff --git a/Cargo.toml b/Cargo.toml index 13853dd..656d33f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,12 +11,4 @@ keywords = ["xdg", "basedir", "app_dirs", "path", "folder"] [dependencies] cfg-if = "0.1" - -[target.'cfg(unix)'.dependencies] -libc = "0.2" - -[target.'cfg(target_os = "redox")'.dependencies] -redox_users = "0.3.0" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["knownfolders", "objbase", "shlobj", "winbase", "winerror"] } +dirs-sys = "" diff --git a/README.md b/README.md index c9f249e..2b6d44c 100644 --- a/README.md +++ b/README.md @@ -155,8 +155,9 @@ The following commands will build this library on Linux, macOS and Windows: ``` cargo build --target=x86_64-unknown-linux-gnu -cargo build --target=x86_64-apple-darwin cargo build --target=x86_64-pc-windows-gnu +cargo build --target=x86_64-apple-darwin +cargo build --target=x86_64-unknown-redox ``` ## License diff --git a/src/lib.rs b/src/lib.rs index 762e0e2..3d21de9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ //! - a tiny library with a minimal API (16 functions) //! - that provides the platform-specific, user-accessible locations //! - for finding and storing configuration, cache and other data -//! - on Linux, Windows (≥ Vista) and macOS. +//! - on Linux, Redox, Windows (≥ Vista) and macOS. //! //! The library provides the location of these directories by leveraging the mechanisms defined by //! @@ -18,9 +18,6 @@ extern crate cfg_if; use std::path::PathBuf; -#[cfg(unix)] -mod unix; - cfg_if! { if #[cfg(target_os = "windows")] { mod win; @@ -28,9 +25,6 @@ cfg_if! { } else if #[cfg(target_os = "macos")] { mod mac; use mac as sys; - } else if #[cfg(target_os = "redox")] { - mod redox; - use redox as sys; } else if #[cfg(target_os = "wasi")] { mod wasi; use wasi as sys; diff --git a/src/lin.rs b/src/lin.rs index b178692..74ac610 100644 --- a/src/lin.rs +++ b/src/lin.rs @@ -1,52 +1,28 @@ +extern crate dirs_sys; + use std::env; -use std::ffi::OsString; use std::path::PathBuf; -use std::process::Command; - -use unix; -pub fn home_dir() -> Option { unix::home_dir() } -pub fn cache_dir() -> Option { env::var_os("XDG_CACHE_HOME") .and_then(is_absolute_path).or_else(|| home_dir().map(|h| h.join(".cache"))) } -pub fn config_dir() -> Option { env::var_os("XDG_CONFIG_HOME").and_then(is_absolute_path).or_else(|| home_dir().map(|h| h.join(".config"))) } -pub fn data_dir() -> Option { env::var_os("XDG_DATA_HOME") .and_then(is_absolute_path).or_else(|| home_dir().map(|h| h.join(".local/share"))) } +pub fn home_dir() -> Option { dirs_sys::home_dir() } +pub fn cache_dir() -> Option { env::var_os("XDG_CACHE_HOME") .and_then(dirs_sys::is_absolute_path).or_else(|| home_dir().map(|h| h.join(".cache"))) } +pub fn config_dir() -> Option { env::var_os("XDG_CONFIG_HOME").and_then(dirs_sys::is_absolute_path).or_else(|| home_dir().map(|h| h.join(".config"))) } +pub fn data_dir() -> Option { env::var_os("XDG_DATA_HOME") .and_then(dirs_sys::is_absolute_path).or_else(|| home_dir().map(|h| h.join(".local/share"))) } pub fn data_local_dir() -> Option { data_dir().clone() } -pub fn runtime_dir() -> Option { env::var_os("XDG_RUNTIME_DIR").and_then(is_absolute_path) } +pub fn runtime_dir() -> Option { env::var_os("XDG_RUNTIME_DIR").and_then(dirs_sys::is_absolute_path) } pub fn executable_dir() -> Option { - env::var_os("XDG_BIN_HOME").and_then(is_absolute_path).or_else(|| { + env::var_os("XDG_BIN_HOME").and_then(dirs_sys::is_absolute_path).or_else(|| { data_dir().map(|mut e| { e.pop(); e.push("bin"); e }) }) } -pub fn audio_dir() -> Option { run_xdg_user_dir_command("MUSIC") } -pub fn desktop_dir() -> Option { run_xdg_user_dir_command("DESKTOP") } -pub fn document_dir() -> Option { run_xdg_user_dir_command("DOCUMENTS") } -pub fn download_dir() -> Option { run_xdg_user_dir_command("DOWNLOAD") } +pub fn audio_dir() -> Option { dirs_sys::user_dir("MUSIC") } +pub fn desktop_dir() -> Option { dirs_sys::user_dir("DESKTOP") } +pub fn document_dir() -> Option { dirs_sys::user_dir("DOCUMENTS") } +pub fn download_dir() -> Option { dirs_sys::user_dir("DOWNLOAD") } pub fn font_dir() -> Option { data_dir().map(|d| d.join("fonts")) } -pub fn picture_dir() -> Option { run_xdg_user_dir_command("PICTURES") } -pub fn public_dir() -> Option { run_xdg_user_dir_command("PUBLICSHARE") } -pub fn template_dir() -> Option { run_xdg_user_dir_command("TEMPLATES") } -pub fn video_dir() -> Option { run_xdg_user_dir_command("VIDEOS") } - -// we don't need to explicitly handle empty strings in the code above, -// because an empty string is not considered to be a absolute path here. -fn is_absolute_path(path: OsString) -> Option { - let path = PathBuf::from(path); - if path.is_absolute() { - Some(path) - } else { - None - } -} - -fn run_xdg_user_dir_command(arg: &str) -> Option { - use std::os::unix::ffi::OsStringExt; - let mut out = match Command::new("xdg-user-dir").arg(arg).output() { - Ok(output) => output.stdout, - Err(_) => return None, - }; - let out_len = out.len(); - out.truncate(out_len - 1); - Some(PathBuf::from(OsString::from_vec(out))) -} +pub fn picture_dir() -> Option { dirs_sys::user_dir("PICTURES") } +pub fn public_dir() -> Option { dirs_sys::user_dir("PUBLICSHARE") } +pub fn template_dir() -> Option { dirs_sys::user_dir("TEMPLATES") } +pub fn video_dir() -> Option { dirs_sys::user_dir("VIDEOS") } #[cfg(test)] mod tests { diff --git a/src/mac.rs b/src/mac.rs index b16bbd9..ae7f9c9 100644 --- a/src/mac.rs +++ b/src/mac.rs @@ -1,8 +1,8 @@ -use std::path::PathBuf; +extern crate dirs_sys; -use unix; +use std::path::PathBuf; -pub fn home_dir() -> Option { unix::home_dir() } +pub fn home_dir() -> Option { dirs_sys::home_dir() } pub fn cache_dir() -> Option { home_dir().map(|h| h.join("Library/Caches")) } pub fn config_dir() -> Option { home_dir().map(|h| h.join("Library/Preferences")) } pub fn data_dir() -> Option { home_dir().map(|h| h.join("Library/Application Support")) } diff --git a/src/redox.rs b/src/redox.rs deleted file mode 100644 index 7475192..0000000 --- a/src/redox.rs +++ /dev/null @@ -1,46 +0,0 @@ -extern crate redox_users; - -use std::env; -use std::ffi::OsString; -use std::path::PathBuf; -use self::redox_users::All; -use self::redox_users::AllUsers; -use self::redox_users::Config; - -pub fn home_dir() -> Option { - let current_uid = redox_users::get_uid().ok()?; - let users = AllUsers::new(Config::default()).ok()?; - let user = users.get_by_id(current_uid)?; - - Some(PathBuf::from(user.home.clone())) -} -pub fn cache_dir() -> Option { env::var_os("XDG_CACHE_HOME") .and_then(is_absolute_path).or_else(|| home_dir().map(|h| h.join(".cache"))) } -pub fn config_dir() -> Option { env::var_os("XDG_CONFIG_HOME").and_then(is_absolute_path).or_else(|| home_dir().map(|h| h.join(".config"))) } -pub fn data_dir() -> Option { env::var_os("XDG_DATA_HOME") .and_then(is_absolute_path).or_else(|| home_dir().map(|h| h.join(".local/share"))) } -pub fn data_local_dir() -> Option { data_dir().clone() } -pub fn runtime_dir() -> Option { env::var_os("XDG_RUNTIME_DIR").and_then(is_absolute_path) } -pub fn executable_dir() -> Option { - env::var_os("XDG_BIN_HOME").and_then(is_absolute_path).or_else(|| { - data_dir().map(|mut e| { e.pop(); e.push("bin"); e }) - }) -} -pub fn audio_dir() -> Option { home_dir().map(|h| h.join("Music")) } -pub fn desktop_dir() -> Option { home_dir().map(|h| h.join("Desktop")) } -pub fn document_dir() -> Option { home_dir().map(|h| h.join("Documents")) } -pub fn download_dir() -> Option { home_dir().map(|h| h.join("Downloads")) } -pub fn font_dir() -> Option { data_dir().map(|d| d.join("fonts")) } -pub fn picture_dir() -> Option { home_dir().map(|h| h.join("Pictures")) } -pub fn public_dir() -> Option { home_dir().map(|h| h.join("Public")) } -pub fn template_dir() -> Option { home_dir().map(|h| h.join("Templates")) } -pub fn video_dir() -> Option { home_dir().map(|h| h.join("Videos")) } - -// we don't need to explicitly handle empty strings in the code above, -// because an empty string is not considered to be a absolute path here. -fn is_absolute_path(path: OsString) -> Option { - let path = PathBuf::from(path); - if path.is_absolute() { - Some(path) - } else { - None - } -} diff --git a/src/unix.rs b/src/unix.rs deleted file mode 100644 index 9068ed7..0000000 --- a/src/unix.rs +++ /dev/null @@ -1,52 +0,0 @@ -#![cfg(unix)] - -use std::env; -use std::ffi::CStr; -use std::ffi::OsString; -use std::mem; -use std::os::unix::ffi::OsStringExt; -use std::path::PathBuf; -use std::ptr; - -extern crate libc; - -// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/os.rs#L498 -pub fn home_dir() -> Option { - return env::var_os("HOME") - .and_then(|h| if h.is_empty() { None } else { Some(h) }) - .or_else(|| unsafe { fallback() }) - .map(PathBuf::from); - - #[cfg(any(target_os = "android", target_os = "ios", target_os = "emscripten"))] - unsafe fn fallback() -> Option { - None - } - #[cfg(not(any(target_os = "android", target_os = "ios", target_os = "emscripten")))] - unsafe fn fallback() -> Option { - let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { - n if n < 0 => 512 as usize, - n => n as usize, - }; - let mut buf = Vec::with_capacity(amt); - let mut passwd: libc::passwd = mem::zeroed(); - let mut result = ptr::null_mut(); - match libc::getpwuid_r( - libc::getuid(), - &mut passwd, - buf.as_mut_ptr(), - buf.capacity(), - &mut result, - ) { - 0 if !result.is_null() => { - let ptr = passwd.pw_dir as *const _; - let bytes = CStr::from_ptr(ptr).to_bytes(); - if bytes.is_empty() { - None - } else { - Some(OsStringExt::from_vec(bytes.to_vec())) - } - } - _ => None, - } - } -} diff --git a/src/wasi.rs b/src/wasi.rs index e0e8ace..2ead720 100644 --- a/src/wasi.rs +++ b/src/wasi.rs @@ -1,6 +1,4 @@ -// TODO: flesh this out once actual WASI APIs for this are designed. -// -// For now here are just enough definitions to make things *compile*. +// Stub definitions to make things *compile*. use std::path::PathBuf; diff --git a/src/win.rs b/src/win.rs index 07a706e..a62e95b 100644 --- a/src/win.rs +++ b/src/win.rs @@ -1,44 +1,20 @@ -use std; -use std::path::PathBuf; +extern crate dirs_sys; -extern crate winapi; -use self::winapi::shared::winerror; -use self::winapi::um::knownfolders; -use self::winapi::um::combaseapi; -use self::winapi::um::shlobj; -use self::winapi::um::shtypes; -use self::winapi::um::winbase; -use self::winapi::um::winnt; +use std::path::PathBuf; -pub fn home_dir() -> Option { known_folder(&knownfolders::FOLDERID_Profile) } -pub fn data_dir() -> Option { known_folder(&knownfolders::FOLDERID_RoamingAppData) } -pub fn data_local_dir() -> Option { known_folder(&knownfolders::FOLDERID_LocalAppData) } +pub fn home_dir() -> Option { dirs_sys::known_folder_profile() } +pub fn data_dir() -> Option { dirs_sys::known_folder_roaming_app_data() } +pub fn data_local_dir() -> Option { dirs_sys::known_folder_local_app_data() } pub fn cache_dir() -> Option { data_local_dir() } pub fn config_dir() -> Option { data_dir() } pub fn executable_dir() -> Option { None } pub fn runtime_dir() -> Option { None } -pub fn audio_dir() -> Option { known_folder(&knownfolders::FOLDERID_Music) } -pub fn desktop_dir() -> Option { known_folder(&knownfolders::FOLDERID_Desktop) } -pub fn document_dir() -> Option { known_folder(&knownfolders::FOLDERID_Documents) } -pub fn download_dir() -> Option { known_folder(&knownfolders::FOLDERID_Downloads) } +pub fn audio_dir() -> Option { dirs_sys::known_folder_music() } +pub fn desktop_dir() -> Option { dirs_sys::known_folder_desktop() } +pub fn document_dir() -> Option { dirs_sys::known_folder_documents() } +pub fn download_dir() -> Option { dirs_sys::known_folder_downloads() } pub fn font_dir() -> Option { None } -pub fn picture_dir() -> Option { known_folder(&knownfolders::FOLDERID_Pictures) } -pub fn public_dir() -> Option { known_folder(&knownfolders::FOLDERID_Public) } -pub fn template_dir() -> Option { known_folder(&knownfolders::FOLDERID_Templates) } -pub fn video_dir() -> Option { known_folder(&knownfolders::FOLDERID_Videos) } - -fn known_folder(folder_id: shtypes::REFKNOWNFOLDERID) -> Option { - unsafe { - let mut path_ptr: winnt::PWSTR = std::ptr::null_mut(); - let result = shlobj::SHGetKnownFolderPath(folder_id, 0, std::ptr::null_mut(), &mut path_ptr); - if result == winerror::S_OK { - let len = winbase::lstrlenW(path_ptr) as usize; - let path = std::slice::from_raw_parts(path_ptr, len); - let ostr: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(path); - combaseapi::CoTaskMemFree(path_ptr as *mut winapi::ctypes::c_void); - Some(PathBuf::from(ostr)) - } else { - None - } - } -} +pub fn picture_dir() -> Option { dirs_sys::known_folder_pictures() } +pub fn public_dir() -> Option { dirs_sys::known_folder_public()} +pub fn template_dir() -> Option { dirs_sys::known_folder_templates() } +pub fn video_dir() -> Option { dirs_sys::known_folder_videos() }