#[cfg(target_os = "windows")]
extern crate shell32;
#[cfg(target_os = "windows")]
extern crate winapi;
#[cfg(target_os = "windows")]
extern crate ole32;
#[cfg(not(target_os = "windows"))]
use std::env;
use std::path::PathBuf;
#[cfg(target_os = "macos")]
fn home_dir_relative(rel: &str, app: Option<&str>) -> Result<PathBuf, ()> {
let data_dir_res = env::home_dir();
if data_dir_res.is_none() {
return Err(());
}
let mut data_dir = data_dir_res.unwrap();
data_dir.push(rel);
if app.is_some() {
data_dir.push(app.unwrap());
}
Ok(data_dir)
}
#[cfg(target_os = "macos")]
pub fn user_data_dir(app: Option<&str>, _: Option<&str>, _: bool) -> Result<PathBuf, ()> {
home_dir_relative("Library/Application Support", app)
}
#[cfg(target_os = "macos")]
pub fn site_data_dir(app: Option<&str>, _: Option<&str>) -> Result<PathBuf, ()> {
let mut data_dir = PathBuf::new();
data_dir.push("/Library/Application Support");
if app.is_some() {
data_dir.push(app.unwrap());
}
Ok(data_dir)
}
#[cfg(target_os = "macos")]
pub fn user_config_dir(app: Option<&str>, author: Option<&str>, roaming: bool)
-> Result<PathBuf, ()> {
user_data_dir(app, author, roaming)
}
#[cfg(target_os = "macos")]
pub fn site_config_dir(app: Option<&str>, author: Option<&str>) -> Result<PathBuf, ()> {
site_data_dir(app, author)
}
#[cfg(target_os = "macos")]
pub fn user_cache_dir(app: Option<&str>, _: Option<&str>) -> Result<PathBuf, ()> {
home_dir_relative("Library/Caches", app)
}
#[cfg(target_os = "macos")]
pub fn user_log_dir(app: Option<&str>, _: Option<&str>) -> Result<PathBuf, ()> {
home_dir_relative("Library/Logs", app)
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
fn home_dir_relative(xdg_key: &str, rel: &str, app: Option<&str>) -> Result<PathBuf, ()> {
let mut data_dir = PathBuf::new();
match env::var_os(xdg_key) {
Some(dir) => { data_dir.push(dir); },
None => {
let home_res = env::home_dir();
if home_res.is_none() {
return Err(());
}
data_dir.push(home_res.unwrap());
data_dir.push(rel);
},
};
if app.is_some() {
data_dir.push(app.unwrap());
}
Ok(data_dir)
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
pub fn user_data_dir(app: Option<&str>, _: Option<&str>, _: bool) -> Result<PathBuf, ()> {
home_dir_relative("XDG_DATA_HOME", ".local/share", app)
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
pub fn site_data_dir(app: Option<&str>, _: Option<&str>) -> Result<PathBuf, ()> {
let mut data_dir = PathBuf::new();
let default = "/usr/local/share";
match env::var_os("XDG_DATA_DIRS") {
Some(joined) => {
let first = env::split_paths(&joined).next();
match first {
Some(dir) => { data_dir.push(dir); },
None => { data_dir.push(default); },
};
},
None => { data_dir.push(default); },
};
if app.is_some() {
data_dir.push(app.unwrap());
}
Ok(data_dir)
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
pub fn user_config_dir(app: Option<&str>, _: Option<&str>, _: bool) -> Result<PathBuf, ()> {
home_dir_relative("XDG_CONFIG_HOME", ".config", app)
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
pub fn site_config_dir(app: Option<&str>, _: Option<&str>) -> Result<PathBuf, ()> {
let mut data_dir = PathBuf::new();
let default = "/etc/xdg";
match env::var_os("XDG_CONFIG_DIRS") {
Some(joined) => {
let first = env::split_paths(&joined).next();
match first {
Some(dir) => { data_dir.push(dir); },
None => { data_dir.push(default); },
};
},
None => { data_dir.push(default); },
};
if app.is_some() {
data_dir.push(app.unwrap());
}
Ok(data_dir)
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
pub fn user_cache_dir(app: Option<&str>, _: Option<&str>) -> Result<PathBuf, ()> {
home_dir_relative("XDG_CACHE_HOME", ".cache", app)
}
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
pub fn user_log_dir(app: Option<&str>, author: Option<&str>) -> Result<PathBuf, ()> {
let log_dir = user_cache_dir(app, author);
match log_dir {
Ok(mut log_dir) => { log_dir.push("log"); Ok(log_dir) },
Err(err) => Err(err),
}
}
#[cfg(target_os = "windows")]
static APPDATA_GUID: winapi::shtypes::KNOWNFOLDERID = winapi::shtypes::KNOWNFOLDERID {
Data1: 1052149211,
Data2: 26105,
Data3: 19702,
Data4: [160, 58, 227, 239, 101, 114, 159, 61],
};
#[cfg(target_os = "windows")]
static COMMON_APPDATA_GUID: winapi::shtypes::KNOWNFOLDERID = winapi::shtypes::KNOWNFOLDERID {
Data1: 1655397762,
Data2: 64961,
Data3: 19907,
Data4: [169, 221, 7, 13, 29, 73, 93, 151],
};
#[cfg(target_os = "windows")]
static LOCAL_APPDATA_GUID: winapi::shtypes::KNOWNFOLDERID = winapi::shtypes::KNOWNFOLDERID {
Data1: 4055050117,
Data2: 28602,
Data3: 20431,
Data4: [157, 85, 123, 142, 127, 21, 112, 145],
};
#[cfg(target_os = "windows")]
use std::ptr;
#[cfg(target_os = "windows")]
use std::ffi::OsString;
#[cfg(target_os = "windows")]
use std::os::windows::ffi::OsStringExt;
#[cfg(target_os = "windows")]
use std::slice;
#[cfg(target_os = "windows")]
fn get_dir(id: &winapi::shtypes::KNOWNFOLDERID) -> Result<OsString, ()> {
let mut result: winapi::PWSTR = ptr::null_mut();
let error;
unsafe {
error = shell32::SHGetKnownFolderPath(id, 0, ptr::null_mut(),
&mut result);
}
if error != winapi::S_OK {
return Err(());
}
unsafe {
let mut len = 0;
let mut cur = result;
while *cur != 0 {
len += 1;
cur = cur.offset(1);
}
let os_string: OsString =
OsStringExt::from_wide(slice::from_raw_parts(result, len));
ole32::CoTaskMemFree(result as *mut _);
Ok(os_string)
}
}
#[cfg(target_os = "windows")]
pub fn user_data_dir(app: Option<&str>, author: Option<&str>, roaming: bool)
-> Result<PathBuf, ()> {
let dir_id = if roaming { APPDATA_GUID } else { LOCAL_APPDATA_GUID };
let mut data_dir = PathBuf::new();
data_dir.push(try!(get_dir(&dir_id)));
if author.is_some() {
data_dir.push(author.unwrap());
}
if app.is_some() {
data_dir.push(app.unwrap());
}
Ok(data_dir)
}
#[cfg(target_os = "windows")]
pub fn site_data_dir(app: Option<&str>, author: Option<&str>) -> Result<PathBuf, ()> {
let mut data_dir = PathBuf::new();
data_dir.push(try!(get_dir(&COMMON_APPDATA_GUID)));
if author.is_some() {
data_dir.push(author.unwrap());
}
if app.is_some() {
data_dir.push(app.unwrap());
}
Ok(data_dir)
}
#[cfg(target_os = "windows")]
pub fn user_config_dir(app: Option<&str>, author: Option<&str>, roaming: bool)
-> Result<PathBuf, ()> {
user_data_dir(app, author, roaming)
}
#[cfg(target_os = "windows")]
pub fn site_config_dir(app: Option<&str>, author: Option<&str>) -> Result<PathBuf, ()> {
site_data_dir(app, author)
}
#[cfg(target_os = "windows")]
pub fn user_cache_dir(app: Option<&str>, author: Option<&str>) -> Result<PathBuf, ()> {
let cache_dir = user_data_dir(app, author, false);
match cache_dir {
Ok(mut cache_dir) => { cache_dir.push("Cache"); Ok(cache_dir) },
Err(err) => Err(err),
}
}
#[cfg(target_os = "windows")]
pub fn user_log_dir(app: Option<&str>, author: Option<&str>) -> Result<PathBuf, ()> {
let mut log_dir = user_data_dir(app, author, false);
match log_dir {
Ok(mut log_dir) => { log_dir.push("Logs"); Ok(log_dir) },
Err(err) => Err(err),
}
}
#[cfg(test)]
mod tests {
use std::io::{self, Write};
use std::path::PathBuf;
fn to_stderr(name: &str, value: PathBuf) {
let _ = io::stderr().write(format!("{}: {}\n", name,
value.to_str().unwrap()).as_bytes());
}
#[test]
fn output_dirs() {
to_stderr("user data dir",
super::user_data_dir(Some("AppDirs"), Some("djc"), false).unwrap());
to_stderr("site data dir",
super::site_data_dir(Some("AppDirs"), Some("djc")).unwrap());
to_stderr("user config dir",
super::user_config_dir(Some("AppDirs"), Some("djc"), false).unwrap());
to_stderr("site config dir",
super::site_config_dir(Some("AppDirs"), Some("djc")).unwrap());
to_stderr("user cache dir",
super::user_cache_dir(Some("AppDirs"), Some("djc")).unwrap());
to_stderr("user log dir",
super::user_log_dir(Some("AppDirs"), Some("djc")).unwrap());
}
}