#![cfg(any(unix, target_os = "redox"))]
use std::fmt;
use std::convert;
use std::error;
use std::io;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::ffi::OsString;
use std::os::unix::fs::PermissionsExt;
use BaseDirectoriesErrorKind::*;
use BaseDirectoriesError as Error;
#[derive(Debug, Clone)]
pub struct BaseDirectories {
shared_prefix: PathBuf,
user_prefix: PathBuf,
data_home: PathBuf,
config_home: PathBuf,
cache_home: PathBuf,
data_dirs: Vec<PathBuf>,
config_dirs: Vec<PathBuf>,
runtime_dir: Option<PathBuf>,
}
pub struct BaseDirectoriesError {
kind: BaseDirectoriesErrorKind,
}
impl BaseDirectoriesError {
fn new(kind: BaseDirectoriesErrorKind) -> BaseDirectoriesError {
BaseDirectoriesError {
kind: kind,
}
}
}
impl fmt::Debug for BaseDirectoriesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.kind.fmt(f)
}
}
impl error::Error for BaseDirectoriesError {
fn description(&self) -> &str {
match self.kind {
HomeMissing => "$HOME must be set",
XdgRuntimeDirInaccessible(_, _) =>
"$XDG_RUNTIME_DIR must be accessible by the current user",
XdgRuntimeDirInsecure(_, _) =>
"$XDG_RUNTIME_DIR must be secure: have permissions 0700",
XdgRuntimeDirMissing =>
"$XDG_RUNTIME_DIR is not set",
}
}
fn cause(&self) -> Option<&error::Error> {
match self.kind {
XdgRuntimeDirInaccessible(_, ref e) => Some(e),
_ => None,
}
}
}
impl fmt::Display for BaseDirectoriesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
HomeMissing => write!(f, "{}", error::Error::description(self)),
XdgRuntimeDirInaccessible(ref dir, ref error) => {
write!(f, "$XDG_RUNTIME_DIR (`{}`) must be accessible \
by the current user (error: {})", dir.display(), error)
},
XdgRuntimeDirInsecure(ref dir, permissions) => {
write!(f, "$XDG_RUNTIME_DIR (`{}`) must be secure: must have \
permissions 0o700, got {}", dir.display(), permissions)
},
XdgRuntimeDirMissing => {
write!(f, "$XDG_RUNTIME_DIR must be set")
},
}
}
}
impl convert::From<BaseDirectoriesError> for io::Error {
fn from(error: BaseDirectoriesError) -> io::Error {
match error.kind {
HomeMissing | XdgRuntimeDirMissing =>
io::Error::new(io::ErrorKind::NotFound, error),
_ => io::Error::new(io::ErrorKind::Other, error)
}
}
}
#[derive(Copy, Clone)]
struct Permissions(u32);
impl fmt::Debug for Permissions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Permissions(p) = *self;
write!(f, "{:#05o}", p)
}
}
impl fmt::Display for Permissions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Debug)]
enum BaseDirectoriesErrorKind {
HomeMissing,
XdgRuntimeDirInaccessible(PathBuf, io::Error),
XdgRuntimeDirInsecure(PathBuf, Permissions),
XdgRuntimeDirMissing,
}
impl BaseDirectories {
pub fn new() -> Result<BaseDirectories, BaseDirectoriesError> {
BaseDirectories::with_env("", "", &|name| env::var_os(name))
}
pub fn with_prefix<P>(prefix: P) -> Result<BaseDirectories, BaseDirectoriesError>
where P: AsRef<Path> {
BaseDirectories::with_env(prefix, "", &|name| env::var_os(name))
}
pub fn with_profile<P1, P2>(prefix: P1, profile: P2)
-> Result<BaseDirectories, BaseDirectoriesError>
where P1: AsRef<Path>, P2: AsRef<Path> {
BaseDirectories::with_env(prefix, profile, &|name| env::var_os(name))
}
fn with_env<P1, P2, T: ?Sized>(prefix: P1, profile: P2, env_var: &T)
-> Result<BaseDirectories, BaseDirectoriesError>
where P1: AsRef<Path>, P2: AsRef<Path>, T: Fn(&str) -> Option<OsString> {
BaseDirectories::with_env_impl(prefix.as_ref(), profile.as_ref(), env_var)
}
fn with_env_impl<T: ?Sized>(prefix: &Path, profile: &Path, env_var: &T)
-> Result<BaseDirectories, BaseDirectoriesError>
where T: Fn(&str) -> Option<OsString> {
fn abspath(path: OsString) -> Option<PathBuf> {
let path = PathBuf::from(path);
if path.is_absolute() {
Some(path)
} else {
None
}
}
fn abspaths(paths: OsString) -> Option<Vec<PathBuf>> {
let paths = env::split_paths(&paths)
.map(PathBuf::from)
.filter(|ref path| path.is_absolute())
.collect::<Vec<_>>();
if paths.is_empty() {
None
} else {
Some(paths)
}
}
let home = try!(std::env::home_dir().ok_or(Error::new(HomeMissing)));
let data_home = env_var("XDG_DATA_HOME")
.and_then(abspath)
.unwrap_or(home.join(".local/share"));
let config_home = env_var("XDG_CONFIG_HOME")
.and_then(abspath)
.unwrap_or(home.join(".config"));
let cache_home = env_var("XDG_CACHE_HOME")
.and_then(abspath)
.unwrap_or(home.join(".cache"));
let data_dirs = env_var("XDG_DATA_DIRS")
.and_then(abspaths)
.unwrap_or(vec![PathBuf::from("/usr/local/share"),
PathBuf::from("/usr/share")]);
let config_dirs = env_var("XDG_CONFIG_DIRS")
.and_then(abspaths)
.unwrap_or(vec![PathBuf::from("/etc/xdg")]);
let runtime_dir = env_var("XDG_RUNTIME_DIR")
.and_then(abspath);
let prefix = PathBuf::from(prefix);
Ok(BaseDirectories {
user_prefix: prefix.join(profile),
shared_prefix: prefix,
data_home: data_home,
config_home: config_home,
cache_home: cache_home,
data_dirs: data_dirs,
config_dirs: config_dirs,
runtime_dir: runtime_dir,
})
}
fn get_runtime_directory(&self) -> Result<&PathBuf, BaseDirectoriesError> {
if let Some(ref runtime_dir) = self.runtime_dir {
try!(fs::read_dir(runtime_dir).map_err(|e| {
Error::new(XdgRuntimeDirInaccessible(runtime_dir.clone(), e))
}));
let permissions = try!(fs::metadata(runtime_dir).map_err(|e| {
Error::new(XdgRuntimeDirInaccessible(runtime_dir.clone(), e))
})).permissions().mode() as u32;
if permissions & 0o077 != 0 {
Err(Error::new(XdgRuntimeDirInsecure(runtime_dir.clone(),
Permissions(permissions))))
} else {
Ok(&runtime_dir)
}
} else {
Err(Error::new(XdgRuntimeDirMissing))
}
}
pub fn has_runtime_directory(&self) -> bool {
match self.get_runtime_directory() {
Ok(_) => true,
_ => false
}
}
pub fn place_config_file<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
write_file(&self.config_home, self.user_prefix.join(path))
}
pub fn place_data_file<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
write_file(&self.data_home, self.user_prefix.join(path))
}
pub fn place_cache_file<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
write_file(&self.cache_home, self.user_prefix.join(path))
}
pub fn place_runtime_file<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
write_file(try!(self.get_runtime_directory()), self.user_prefix.join(path))
}
pub fn find_config_file<P>(&self, path: P) -> Option<PathBuf>
where P: AsRef<Path> {
read_file(&self.config_home, &self.config_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn find_config_files<P>(&self, path: P) -> FileFindIterator
where P: AsRef<Path> {
FileFindIterator::new(&self.config_home, &self.config_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn find_data_file<P>(&self, path: P) -> Option<PathBuf>
where P: AsRef<Path> {
read_file(&self.data_home, &self.data_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn find_data_files<P>(&self, path: P) -> FileFindIterator
where P: AsRef<Path> {
FileFindIterator::new(&self.data_home, &self.data_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn find_cache_file<P>(&self, path: P) -> Option<PathBuf>
where P: AsRef<Path> {
read_file(&self.cache_home, &Vec::new(),
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn find_runtime_file<P>(&self, path: P) -> Option<PathBuf>
where P: AsRef<Path> {
if let Ok(runtime_dir) = self.get_runtime_directory() {
read_file(runtime_dir, &Vec::new(),
&self.user_prefix, &self.shared_prefix, path.as_ref())
} else {
None
}
}
pub fn create_config_directory<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
create_directory(&self.config_home,
self.user_prefix.join(path))
}
pub fn create_data_directory<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
create_directory(&self.data_home,
self.user_prefix.join(path))
}
pub fn create_cache_directory<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
create_directory(&self.cache_home,
self.user_prefix.join(path))
}
pub fn create_runtime_directory<P>(&self, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
create_directory(try!(self.get_runtime_directory()),
self.user_prefix.join(path))
}
pub fn list_config_files<P>(&self, path: P) -> Vec<PathBuf>
where P: AsRef<Path> {
list_files(&self.config_home, &self.config_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn list_config_files_once<P>(&self, path: P) -> Vec<PathBuf>
where P: AsRef<Path> {
list_files_once(&self.config_home, &self.config_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn list_data_files<P>(&self, path: P) -> Vec<PathBuf>
where P: AsRef<Path> {
list_files(&self.data_home, &self.data_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn list_data_files_once<P>(&self, path: P) -> Vec<PathBuf>
where P: AsRef<Path> {
list_files_once(&self.data_home, &self.data_dirs,
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn list_cache_files<P>(&self, path: P) -> Vec<PathBuf>
where P: AsRef<Path> {
list_files(&self.cache_home, &Vec::new(),
&self.user_prefix, &self.shared_prefix, path.as_ref())
}
pub fn list_runtime_files<P>(&self, path: P) -> Vec<PathBuf>
where P: AsRef<Path> {
if let Ok(runtime_dir) = self.get_runtime_directory() {
list_files(runtime_dir, &Vec::new(),
&self.user_prefix, &self.shared_prefix, path.as_ref())
} else {
Vec::new()
}
}
pub fn get_data_home(&self) -> PathBuf {
self.data_home.join(&self.user_prefix)
}
pub fn get_config_home(&self) -> PathBuf {
self.config_home.join(&self.user_prefix)
}
pub fn get_cache_home(&self) -> PathBuf {
self.cache_home.join(&self.user_prefix)
}
pub fn get_data_dirs(&self) -> Vec<PathBuf> {
self.data_dirs.iter().map(|p| p.join(&self.shared_prefix)).collect()
}
pub fn get_config_dirs(&self) -> Vec<PathBuf> {
self.config_dirs.iter().map(|p| p.join(&self.shared_prefix)).collect()
}
}
fn write_file<P>(home: &PathBuf, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
match path.as_ref().parent() {
Some(parent) => try!(fs::create_dir_all(home.join(parent))),
None => try!(fs::create_dir_all(home)),
}
Ok(PathBuf::from(home.join(path.as_ref())))
}
fn create_directory<P>(home: &PathBuf, path: P) -> io::Result<PathBuf>
where P: AsRef<Path> {
let full_path = home.join(path.as_ref());
try!(fs::create_dir_all(&full_path));
Ok(full_path)
}
fn path_exists<P: ?Sized + AsRef<Path>>(path: &P) -> bool {
fn inner(path: &Path) -> bool {
fs::metadata(path).is_ok()
}
inner(path.as_ref())
}
#[cfg(test)]
fn path_is_dir<P: ?Sized + AsRef<Path>>(path: &P) -> bool {
fn inner(path: &Path) -> bool {
fs::metadata(path).map(|m| m.is_dir()).unwrap_or(false)
}
inner(path.as_ref())
}
fn read_file(home: &PathBuf, dirs: &Vec<PathBuf>,
user_prefix: &Path, shared_prefix: &Path, path: &Path)
-> Option<PathBuf> {
let full_path = home.join(user_prefix).join(path);
if path_exists(&full_path) {
return Some(full_path)
}
for dir in dirs.iter() {
let full_path = dir.join(shared_prefix).join(path);
if path_exists(&full_path) {
return Some(full_path)
}
}
None
}
use std::iter::Iterator;
pub struct FileFindIterator {
search_dirs: Vec<PathBuf>,
position: usize,
relpath: PathBuf,
}
impl FileFindIterator {
fn new(home: &PathBuf, dirs: &Vec<PathBuf>,
user_prefix: &Path, shared_prefix: &Path, path: &Path)
-> FileFindIterator {
let mut search_dirs = Vec::new();
for dir in dirs.iter().rev() {
search_dirs.push(dir.join(shared_prefix));
}
search_dirs.push(home.join(user_prefix));
FileFindIterator {
search_dirs: search_dirs,
position: 0,
relpath: path.to_path_buf(),
}
}
}
impl Iterator for FileFindIterator {
type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> {
loop {
let dir = match self.search_dirs.get(self.position) {
Some(d) => d,
None => return None
};
self.position += 1;
let candidate = dir.join(self.relpath.clone());
if path_exists(&candidate) {
return Some(candidate)
}
}
}
}
fn list_files(home: &Path, dirs: &[PathBuf],
user_prefix: &Path, shared_prefix: &Path, path: &Path)
-> Vec<PathBuf> {
fn read_dir(dir: &Path, into: &mut Vec<PathBuf>) {
if let Ok(entries) = fs::read_dir(dir) {
into.extend(
entries
.filter_map(|entry| entry.ok())
.map(|entry| entry.path()))
}
}
let mut files = Vec::new();
read_dir(&home.join(user_prefix).join(path), &mut files);
for dir in dirs {
read_dir(&dir.join(shared_prefix).join(path), &mut files);
}
files
}
fn list_files_once(home: &Path, dirs: &[PathBuf],
user_prefix: &Path, shared_prefix: &Path, path: &Path)
-> Vec<PathBuf> {
let mut seen = std::collections::HashSet::new();
list_files(home, dirs, user_prefix, shared_prefix, path).into_iter().filter(|path| {
match path.clone().file_name() {
None => false,
Some(filename) => {
if seen.contains(filename) {
false
} else {
seen.insert(filename.to_owned());
true
}
}
}
}).collect::<Vec<_>>()
}
#[cfg(test)]
fn make_absolute<P>(path: P) -> PathBuf where P: AsRef<Path> {
env::current_dir().unwrap().join(path.as_ref())
}
#[cfg(test)]
fn iter_after<A, I, J>(mut iter: I, mut prefix: J) -> Option<I> where
I: Iterator<Item=A> + Clone, J: Iterator<Item=A>, A: PartialEq
{
loop {
let mut iter_next = iter.clone();
match (iter_next.next(), prefix.next()) {
(Some(x), Some(y)) => {
if x != y { return None }
}
(Some(_), None) => return Some(iter),
(None, None) => return Some(iter),
(None, Some(_)) => return None,
}
iter = iter_next;
}
}
#[cfg(test)]
fn make_relative<P>(path: P) -> PathBuf where P: AsRef<Path> {
iter_after(path.as_ref().components(), env::current_dir().unwrap().components())
.unwrap().as_path().to_owned()
}
#[cfg(test)]
fn make_env(vars: Vec<(&'static str, String)>) ->
Box<Fn(&str)->Option<OsString>> {
return Box::new(move |name| {
for &(key, ref value) in vars.iter() {
if key == name { return Some(OsString::from(value)) }
}
None
})
}
#[test]
fn test_files_exists() {
assert!(path_exists("test_files"));
assert!(fs::metadata("test_files/runtime-bad")
.unwrap().permissions().mode() & 0o077 != 0);
}
#[test]
fn test_bad_environment() {
let xd = BaseDirectories::with_env("", "", &*make_env(vec![
("HOME", "test_files/user".to_string()),
("XDG_DATA_HOME", "test_files/user/data".to_string()),
("XDG_CONFIG_HOME", "test_files/user/config".to_string()),
("XDG_CACHE_HOME", "test_files/user/cache".to_string()),
("XDG_DATA_DIRS", "test_files/user/data".to_string()),
("XDG_CONFIG_DIRS", "test_files/user/config".to_string()),
("XDG_RUNTIME_DIR", "test_files/runtime-bad".to_string())
])).unwrap();
assert_eq!(xd.find_data_file("everywhere"), None);
assert_eq!(xd.find_config_file("everywhere"), None);
assert_eq!(xd.find_cache_file("everywhere"), None);
}
#[test]
fn test_good_environment() {
let cwd = env::current_dir().unwrap().to_string_lossy().into_owned();
let xd = BaseDirectories::with_env("", "", &*make_env(vec![
("HOME", format!("{}/test_files/user", cwd)),
("XDG_DATA_HOME", format!("{}/test_files/user/data", cwd)),
("XDG_CONFIG_HOME", format!("{}/test_files/user/config", cwd)),
("XDG_CACHE_HOME", format!("{}/test_files/user/cache", cwd)),
("XDG_DATA_DIRS", format!("{}/test_files/system0/data:{}/test_files/system1/data:{}/test_files/system2/data:{}/test_files/system3/data", cwd, cwd, cwd, cwd)),
("XDG_CONFIG_DIRS", format!("{}/test_files/system0/config:{}/test_files/system1/config:{}/test_files/system2/config:{}/test_files/system3/config", cwd, cwd, cwd, cwd)),
])).unwrap();
assert!(xd.find_data_file("everywhere") != None);
assert!(xd.find_config_file("everywhere") != None);
assert!(xd.find_cache_file("everywhere") != None);
let mut config_files = xd.find_config_files("everywhere");
assert_eq!(config_files.next(),
Some(PathBuf::from(format!("{}/test_files/system2/config/everywhere", cwd))));
assert_eq!(config_files.next(),
Some(PathBuf::from(format!("{}/test_files/system1/config/everywhere", cwd))));
assert_eq!(config_files.next(),
Some(PathBuf::from(format!("{}/test_files/user/config/everywhere", cwd))));
assert_eq!(config_files.next(), None);
let mut data_files = xd.find_data_files("everywhere");
assert_eq!(data_files.next(),
Some(PathBuf::from(format!("{}/test_files/system2/data/everywhere", cwd))));
assert_eq!(data_files.next(),
Some(PathBuf::from(format!("{}/test_files/system1/data/everywhere", cwd))));
assert_eq!(data_files.next(),
Some(PathBuf::from(format!("{}/test_files/user/data/everywhere", cwd))));
assert_eq!(data_files.next(), None);
}
#[test]
fn test_runtime_bad() {
let cwd = env::current_dir().unwrap().to_string_lossy().into_owned();
let xd = BaseDirectories::with_env("", "", &*make_env(vec![
("HOME", format!("{}/test_files/user", cwd)),
("XDG_RUNTIME_DIR", format!("{}/test_files/runtime-bad", cwd)),
])).unwrap();
assert!(xd.has_runtime_directory() == false);
}
#[test]
fn test_runtime_good() {
use std::fs::File;
let test_runtime_dir = make_absolute(&"test_files/runtime-good");
let _ = fs::remove_dir_all(&test_runtime_dir);
fs::create_dir_all(&test_runtime_dir).unwrap();
let mut perms = fs::metadata(&test_runtime_dir).unwrap().permissions();
perms.set_mode(0o700);
fs::set_permissions(&test_runtime_dir, perms).unwrap();
let cwd = env::current_dir().unwrap().to_string_lossy().into_owned();
let xd = BaseDirectories::with_env("", "", &*make_env(vec![
("HOME", format!("{}/test_files/user", cwd)),
("XDG_RUNTIME_DIR", format!("{}/test_files/runtime-good", cwd)),
])).unwrap();
xd.create_runtime_directory("foo").unwrap();
assert!(path_is_dir("test_files/runtime-good/foo"));
let w = xd.place_runtime_file("bar/baz").unwrap();
assert!(path_is_dir("test_files/runtime-good/bar"));
assert!(!path_exists("test_files/runtime-good/bar/baz"));
File::create(&w).unwrap();
assert!(path_exists("test_files/runtime-good/bar/baz"));
assert!(xd.find_runtime_file("bar/baz") == Some(w.clone()));
File::open(&w).unwrap();
fs::remove_file(&w).unwrap();
let root = xd.list_runtime_files(".");
let mut root = root.into_iter().map(|p| make_relative(&p)).collect::<Vec<_>>();
root.sort();
assert_eq!(root,
vec![PathBuf::from("test_files/runtime-good/bar"),
PathBuf::from("test_files/runtime-good/foo")]);
assert!(xd.list_runtime_files("bar").is_empty());
assert!(xd.find_runtime_file("foo/qux").is_none());
assert!(xd.find_runtime_file("qux/foo").is_none());
assert!(!path_exists("test_files/runtime-good/qux"));
}
#[test]
fn test_lists() {
let cwd = env::current_dir().unwrap().to_string_lossy().into_owned();
let xd = BaseDirectories::with_env("", "", &*make_env(vec![
("HOME", format!("{}/test_files/user", cwd)),
("XDG_DATA_HOME", format!("{}/test_files/user/data", cwd)),
("XDG_CONFIG_HOME", format!("{}/test_files/user/config", cwd)),
("XDG_CACHE_HOME", format!("{}/test_files/user/cache", cwd)),
("XDG_DATA_DIRS", format!("{}/test_files/system0/data:{}/test_files/system1/data:{}/test_files/system2/data:{}/test_files/system3/data", cwd, cwd, cwd, cwd)),
("XDG_CONFIG_DIRS", format!("{}/test_files/system0/config:{}/test_files/system1/config:{}/test_files/system2/config:{}/test_files/system3/config", cwd, cwd, cwd, cwd)),
])).unwrap();
let files = xd.list_config_files(".");
let mut files = files.into_iter().map(|p| make_relative(&p)).collect::<Vec<_>>();
files.sort();
assert_eq!(files,
[
"test_files/system1/config/both_system_config.file",
"test_files/system1/config/everywhere",
"test_files/system1/config/myapp",
"test_files/system1/config/system1_config.file",
"test_files/system2/config/both_system_config.file",
"test_files/system2/config/everywhere",
"test_files/system2/config/system2_config.file",
"test_files/user/config/everywhere",
"test_files/user/config/myapp",
"test_files/user/config/user_config.file",
].iter().map(PathBuf::from).collect::<Vec<_>>());
let files = xd.list_config_files_once(".");
let mut files = files.into_iter().map(|p| make_relative(&p)).collect::<Vec<_>>();
files.sort();
assert_eq!(files,
[
"test_files/system1/config/both_system_config.file",
"test_files/system1/config/system1_config.file",
"test_files/system2/config/system2_config.file",
"test_files/user/config/everywhere",
"test_files/user/config/myapp",
"test_files/user/config/user_config.file",
].iter().map(PathBuf::from).collect::<Vec<_>>());
}
#[test]
fn test_prefix() {
let cwd = env::current_dir().unwrap().to_string_lossy().into_owned();
let xd = BaseDirectories::with_env("myapp", "", &*make_env(vec![
("HOME", format!("{}/test_files/user", cwd)),
("XDG_CACHE_HOME", format!("{}/test_files/user/cache", cwd)),
])).unwrap();
assert_eq!(xd.place_cache_file("cache.db").unwrap(),
PathBuf::from(&format!("{}/test_files/user/cache/myapp/cache.db", cwd)));
}
#[test]
fn test_profile() {
let cwd = env::current_dir().unwrap().to_string_lossy().into_owned();
let xd = BaseDirectories::with_env("myapp", "default_profile", &*make_env(vec![
("HOME", format!("{}/test_files/user", cwd)),
("XDG_CONFIG_HOME", format!("{}/test_files/user/config", cwd)),
("XDG_CONFIG_DIRS", format!("{}/test_files/system1/config", cwd)),
])).unwrap();
assert_eq!(xd.find_config_file("system1_config.file").unwrap(),
PathBuf::from(&format!("{}/test_files/system1/config/myapp/system1_config.file", cwd)));
assert_eq!(xd.find_config_file("user_config.file").unwrap(),
PathBuf::from(&format!("{}/test_files/user/config/myapp/default_profile/user_config.file", cwd)));
}