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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! # path-lib
//!
//! CROSS-PLATFORM PATHS FOR RUST
//!
//! Where should an application keep its data, config, cache, and logs?
//! The answer differs on every operating system, differs again for a
//! per-user tool versus a machine-wide service, and gets subtly wrong the
//! moment a folder is redirected or an environment variable is missing.
//! `path-lib` answers it once, correctly, with no dependencies.
//!
//! # Application directories
//!
//! ```
//! use path_lib::App;
//!
//! let app = App::new("HiveDB")?; // this user
//! let svc = App::global("HiveDB")?; // the whole machine
//!
//! let data = app.data(); // &Path, resolved once
//! let db = app.data_at("db/main")?; // hardened sub-path
//! # let _ = (svc, data, db);
//! # Ok::<(), path_lib::Error>(())
//! ```
//!
//! | Kind | Linux (user) | macOS (user) | Windows (user) |
//! |-----------|-------------------------------|-----------------------------------------|--------------------------------|
//! | data | `~/.local/share/<app>` | `~/Library/Application Support/<app>` | `%LOCALAPPDATA%\<app>` |
//! | config | `~/.config/<app>` | `~/Library/Preferences/<app>` | `%APPDATA%\<app>` |
//! | cache | `~/.cache/<app>` | `~/Library/Caches/<app>` | `%LOCALAPPDATA%\<app>\cache` |
//! | logs | `~/.local/state/<app>/logs` | `~/Library/Logs/<app>` | `%LOCALAPPDATA%\<app>\logs` |
//! | state | `~/.local/state/<app>` | `.../Application Support/<app>/state` | `%LOCALAPPDATA%\<app>\state` |
//! | runtime | `$XDG_RUNTIME_DIR/<app>` | `.../Application Support/<app>/run` | `%LOCALAPPDATA%\<app>\run` |
//! | temp | `~/.cache/<app>/tmp` | `$TMPDIR/<app>` | `%TEMP%\<app>` |
//! | documents | `~/Documents/<app>` | `~/Documents/<app>` | `Documents\<app>` |
//!
//! | Kind | Linux (global) | macOS (global) | Windows (global) |
//! |-----------|-------------------------|---------------------------------------|--------------------------------|
//! | data | `/var/lib/<app>` | `/Library/Application Support/<app>` | `%ProgramData%\<app>` |
//! | config | `/etc/<app>` | `/Library/Preferences/<app>` | `%ProgramData%\<app>\config` |
//! | cache | `/var/cache/<app>` | `/Library/Caches/<app>` | `%ProgramData%\<app>\cache` |
//! | logs | `/var/log/<app>` | `/Library/Logs/<app>` | `%ProgramData%\<app>\logs` |
//! | state | `/var/lib/<app>/state` | `.../Application Support/<app>/state` | `%ProgramData%\<app>\state` |
//! | runtime | `/run/<app>` | `/var/run/<app>` | `%ProgramData%\<app>\run` |
//! | temp | `/var/tmp/<app>` | `/var/tmp/<app>` | `%ProgramData%\<app>\temp` |
//! | documents | `/srv/<app>` | `/Users/Shared/<app>` | `%PUBLIC%\Documents\<app>` |
//!
//! XDG variables (`XDG_DATA_HOME`, `XDG_CONFIG_HOME`, ...) and
//! `user-dirs.dirs` are honoured on Linux and the BSDs. Windows folders
//! come from the Known Folder API, so OneDrive-redirected Documents
//! resolve correctly. [`App::portable`] puts everything under one folder
//! of your choosing instead, and [`App::auto`] picks a portable layout
//! next to the executable when a [`PORTABLE_MARKER`] file (`.portable`)
//! is there, and per-user directories otherwise.
//!
//! Individual directories can be relocated after construction.
//! [`App::with_dir`] replaces one kind's directory with an absolute path;
//! [`App::with_env`] does the same from `<PREFIX>_<KIND>_DIR` environment
//! variables, which is how services are usually reconfigured in
//! containers. Neither lets two kinds share a directory.
//!
//! ```
//! use path_lib::{App, Kind};
//!
//! let cache = std::env::temp_dir().join("hivedb-cache");
//! let app = App::new("HiveDB")?
//! .with_dir(Kind::Cache, &cache)? // explicit
//! .with_env("HIVEDB")?; // HIVEDB_DATA_DIR, ...
//! # let _ = app;
//! # Ok::<(), path_lib::Error>(())
//! ```
//!
//! # Hardened sub-paths
//!
//! Every `*_at` accessor, and the free function [`join`], accepts a
//! relative path in either slash style and rejects, before building
//! anything, input that could escape the base directory or that means
//! different things on different systems: `..`, drive letters, control
//! characters, Windows-reserved characters and their Unicode look-alikes,
//! bidirectional overrides, device names such as `CON`, and trailing dots
//! or spaces. For names typed by end users, [`sanitize`] repairs instead
//! of rejecting.
//!
//! ```
//! # let app = path_lib::App::portable("Demo", std::env::temp_dir())?;
//! assert!(app.data_at("/big\\sloppy//floppy/").is_ok());
//! assert!(app.data_at("../../etc/passwd").is_err());
//!
//! let name = path_lib::sanitize("Q3: final?.pdf"); // "Q3_ final_.pdf"
//! assert!(app.documents_at(&name).is_ok());
//! # Ok::<(), path_lib::Error>(())
//! ```
//!
//! # OS folders
//!
//! [`home`], [`documents`], [`desktop`], [`downloads`], [`pictures`],
//! [`music`], [`videos`], [`root`], [`temp`], [`exe`], and [`exe_dir`]
//! return cached `&'static Path` values. [`os()`] is a compile-time
//! constant: the operating system never needs detecting at runtime.
//! [`expand_home`] turns `~` and `~/rest` from configuration values into
//! paths under the home directory; it deliberately expands nothing else.
//!
//! # Filesystem operations
//!
//! [`make_dir`], [`make_file`], [`exists`], [`list`], [`remove`],
//! [`copy`], [`move_to`], and [`find`] never overwrite, never follow
//! symbolic links while walking a tree, and refuse to remove or move
//! roots, the home directory, and the user's standard folders. [`App`]
//! exposes the common ones scoped to its directories.
//!
//! [`write_atomic`] is the one operation meant to overwrite: it replaces a
//! file through a flushed temporary sibling and a rename, so readers see
//! either the old contents or the complete new contents, never a partial
//! write.
//!
//! ```
//! let file = std::env::temp_dir().join("path-lib-doc-crate").join("app.conf");
//! path_lib::write_atomic(&file, "port = 7000\n")?;
//! assert_eq!(std::fs::read_to_string(&file).unwrap(), "port = 7000\n");
//! # path_lib::remove(file.parent().unwrap())?;
//! # Ok::<(), path_lib::Error>(())
//! ```
//!
//! # Stability
//!
//! The public API, the directory tables, and the behaviour described
//! above are frozen for every 1.x release. The terms are in
//! `docs/STABILITY-1.0.md` in the repository.
//!
//! # Platform support
//!
//! Linux, macOS, and Windows are tested in CI. FreeBSD and the other
//! BSDs use the Linux (XDG) rules with `/var/run` for global runtime
//! files. Other targets fail to compile with an explanatory message
//! rather than resolving paths that would be wrong.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;