1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use crate::common::{AppDataType, AppDirsError, AppInfo};
use crate::utils;
use std::fs;
use std::path::PathBuf;

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod platform {
    mod macos;
    pub use self::macos::*;
}
#[cfg(all(
    unix,
    not(target_os = "macos"),
    not(target_os = "ios"),
    not(target_os = "android")
))]
mod platform {
    mod unix;
    pub use self::unix::*;
}
#[cfg(windows)]
mod platform {
    mod windows;
    pub use self::windows::*;
}
#[cfg(not(any(windows, unix, target_os = "macos", target_os = "ios")))]
mod platform {
    mod unknown;
    pub use self::unknown::*;
}
#[cfg(target_os = "android")]
mod platform {
    // As per #33, Android tries the Unix path to avoid crashes for programs run
    // inside Termux.
    mod android;
    mod unix;
    pub use self::android::*;
}

/// Creates (if necessary) and returns path to **app-specific** data
/// **subdirectory** for provided data type and subdirectory path.
///
/// The `path` parameter should be a valid relative path separated by
/// **forward slashes** (`/`).
///
/// If the directory structure does not exist, this function will recursively
/// create the full hierarchy. Therefore, a result of `Ok` guarantees that the
/// returned path exists.
pub fn app_dir(t: AppDataType, app: &AppInfo, path: &str) -> Result<PathBuf, AppDirsError> {
    let path = get_app_dir(t, app, path)?;
    match fs::create_dir_all(&path) {
        Ok(..) => Ok(path),
        Err(e) => Err(e.into()),
    }
}

/// Returns (but **does not create**) path to **app-specific** data
/// **subdirectory** for provided data type and subdirectory path.
///
/// The `path` parameter should be a valid relative path separated by
/// **forward slashes** (`/`).
///
/// A result of `Ok` means that we determined where the data SHOULD go, but
/// it DOES NOT guarantee that the directory actually exists. (See
/// [`app_dir`](fn.app_dir.html).)
pub fn get_app_dir(t: AppDataType, app: &AppInfo, path: &str) -> Result<PathBuf, AppDirsError> {
    if app.author.is_empty() || app.name.is_empty() {
        return Err(AppDirsError::InvalidAppInfo);
    }
    get_app_root(t, app).map(|mut root| {
        for component in path.split('/').filter(|s| !s.is_empty()) {
            root.push(utils::sanitized(component));
        }
        root
    })
}

/// Creates (if necessary) and returns path to **app-specific** data
/// directory for provided data type.
///
/// If the directory structure does not exist, this function will recursively
/// create the full hierarchy. Therefore, a result of `Ok` guarantees that the
/// returned path exists.
pub fn app_root(t: AppDataType, app: &AppInfo) -> Result<PathBuf, AppDirsError> {
    let path = get_app_root(t, app)?;
    match fs::create_dir_all(&path) {
        Ok(..) => Ok(path),
        Err(e) => Err(e.into()),
    }
}

/// Returns (but **does not create**) path to **app-specific** data directory
/// for provided data type.
///
/// A result of `Ok` means that we determined where the data SHOULD go, but
/// it DOES NOT guarantee that the directory actually exists. (See
/// [`app_root`](fn.app_root.html).)
pub fn get_app_root(t: AppDataType, app: &AppInfo) -> Result<PathBuf, AppDirsError> {
    if app.author.is_empty() || app.name.is_empty() {
        return Err(AppDirsError::InvalidAppInfo);
    }
    get_data_root(t).map(|mut root| {
        if platform::USE_AUTHOR {
            root.push(utils::sanitized(app.author));
        }
        root.push(utils::sanitized(app.name));
        root
    })
}

/// Creates (if necessary) and returns path to **top-level** data directory
/// for provided data type.
///
/// If the directory structure does not exist, this function will recursively
/// create the full hierarchy. Therefore, a result of `Ok` guarantees that the
/// returned path exists.
pub fn data_root(t: AppDataType) -> Result<PathBuf, AppDirsError> {
    let path = platform::get_app_dir(t)?;
    match fs::create_dir_all(&path) {
        Ok(..) => Ok(path),
        Err(e) => Err(e.into()),
    }
}

/// Returns (but **does not create**) path to **top-level** data directory for
/// provided data type.
///
/// A result of `Ok` means that we determined where the data SHOULD go, but
/// it DOES NOT guarantee that the directory actually exists. (See
/// [`data_root`](fn.data_root.html).)
pub fn get_data_root(t: AppDataType) -> Result<PathBuf, AppDirsError> {
    platform::get_app_dir(t)
}