xdg_user/lib.rs
1#![cfg(any(unix, target_os = "redox"))]
2
3//! This simple crate allows you to get paths to well known user directories,
4//! using [`xdg-user-dirs`][1]s `user-dirs.dirs` file.
5//!
6//! There are two ways of using this crate - with functions in the root of the
7//! crate, or with the [`UserDirs`] struct. [`UserDirs`] will read and parse the
8//! config file only once - when you call the [`UserDirs::new`] function.
9//! Functions in the root will read and parse the config file EVERY TIME you
10//! call them - so use them ONLY if you need to get one or two folders one or
11//! two times.
12//!
13//! # Example
14//!
15//! ```rust
16//! println!("Pictures folder: {:?}", xdg_user::pictures()?);
17//! println!("Music folder: {:?}", xdg_user::music()?);
18//!
19//! let dirs = xdg_user::UserDirs::new()?;
20//! println!("Documents folder: {:?}", dirs.documents());
21//! println!("Downloads folder: {:?}", dirs.downloads());
22//! ```
23//!
24//! [1]: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/
25
26use std::path::{Path, PathBuf};
27
28mod parser;
29mod utils;
30
31/// This crates main and only error type
32#[derive(Debug)]
33pub enum Error {
34 /// Something went wrong while accessing the config file
35 Io(std::io::Error),
36 /// Unable to find the home directory
37 NoHome,
38 /// Error while parsing the config file
39 Parse,
40}
41
42impl From<std::io::Error> for Error {
43 fn from(e: std::io::Error) -> Self {
44 Self::Io(e)
45 }
46}
47
48impl From<std::str::Utf8Error> for Error {
49 fn from(_: std::str::Utf8Error) -> Self {
50 Self::Parse
51 }
52}
53
54impl std::fmt::Display for Error {
55 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
56 match self {
57 Self::Io(e) => e.fmt(f),
58 Self::NoHome => write!(f, "unable to find the home directory"),
59 Self::Parse => write!(f, "error while parsing"),
60 }
61 }
62}
63
64impl std::error::Error for Error {}
65
66/// This crates main and only struct, allows you to access the paths to all the
67/// user directories
68pub struct UserDirs {
69 desktop: Option<PathBuf>,
70 documents: Option<PathBuf>,
71 downloads: Option<PathBuf>,
72 music: Option<PathBuf>,
73 pictures: Option<PathBuf>,
74 public: Option<PathBuf>,
75 templates: Option<PathBuf>,
76 videos: Option<PathBuf>,
77 projects: Option<PathBuf>,
78}
79
80impl std::fmt::Debug for UserDirs {
81 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
82 f.debug_struct("UserDirs").finish()
83 }
84}
85
86impl UserDirs {
87 /// Attempts to read and parse the `${XDG_COFNIG_HOME}/user-dirs.dirs` file
88 pub fn new() -> Result<Self, Error> {
89 let mut this = Self {
90 desktop: None,
91 documents: None,
92 downloads: None,
93 music: None,
94 pictures: None,
95 public: None,
96 templates: None,
97 videos: None,
98 projects: None,
99 };
100
101 utils::parse_file(|key, val| {
102 match key {
103 utils::DESKTOP => this.desktop = val,
104 utils::DOCUMENTS => this.documents = val,
105 utils::DOWNLOADS => this.downloads = val,
106 utils::MUSIC => this.music = val,
107 utils::PICTURES => this.pictures = val,
108 utils::PUBLIC => this.public = val,
109 utils::TEMPLATES => this.templates = val,
110 utils::VIDEOS => this.videos = val,
111 utils::PROJECTS => this.projects = val,
112 _ => {}
113 }
114 true
115 })?;
116
117 Ok(this)
118 }
119
120 /// Returns an absolute path to users desktop directory (`XDG_DESKTOP_DIR`),
121 /// if found
122 pub fn desktop(&self) -> Option<&Path> {
123 self.desktop.as_deref()
124 }
125
126 /// Returns an absolute path to users documents directory
127 /// (`XDG_DOCUMENTS_DIR`), if found
128 pub fn documents(&self) -> Option<&Path> {
129 self.documents.as_deref()
130 }
131
132 /// Returns an absolute path to users downloads directory
133 /// (`XDG_DOWNLOAD_DIR`), if found
134 pub fn downloads(&self) -> Option<&Path> {
135 self.downloads.as_deref()
136 }
137
138 /// Returns an absolute path to users music directory (`XDG_MUSIC_DIR`),
139 /// if found
140 pub fn music(&self) -> Option<&Path> {
141 self.music.as_deref()
142 }
143
144 /// Returns an absolute path to users pictures directory
145 /// (`XDG_PICTURES_DIR`), if found
146 pub fn pictures(&self) -> Option<&Path> {
147 self.pictures.as_deref()
148 }
149
150 /// Returns an absolute path to users public share directory
151 /// (`XDG_PUBLICSHARE_DIR`), if found
152 pub fn public(&self) -> Option<&Path> {
153 self.public.as_deref()
154 }
155
156 /// Returns an absolute path to users templates directory
157 /// (`XDG_TEMPLATES_DIR`), if found
158 pub fn templates(&self) -> Option<&Path> {
159 self.templates.as_deref()
160 }
161
162 /// Returns an absolute path to users videos directory (`XDG_VIDEOS_DIR`),
163 /// if found
164 pub fn videos(&self) -> Option<&Path> {
165 self.videos.as_deref()
166 }
167
168 /// Returns an absolute path to users projects directory
169 /// (`XDG_PROJECTS_DIR`), if found
170 pub fn projects(&self) -> Option<&Path> {
171 self.projects.as_deref()
172 }
173}
174
175fn read_single(env: &[u8]) -> Result<Option<PathBuf>, Error> {
176 let mut ret = None;
177 utils::parse_file(|key, val| {
178 if key == env {
179 ret = val;
180 false
181 } else {
182 true
183 }
184 })?;
185
186 Ok(ret)
187}
188
189/// Returns an absolute path to users desktop directory (`XDG_DESKTOP_DIR`), if
190/// found
191///
192/// <div class="warning">
193///
194/// This function will parse the `user-dirs.dirs` file every time it's called,
195/// so if you need paths to multiple different directories - consider using
196/// [`UserDirs`] instead
197///
198/// </div>
199pub fn desktop() -> Result<Option<PathBuf>, Error> {
200 read_single(utils::DESKTOP)
201}
202
203/// Returns an absolute path to users documents directory (`XDG_DOCUMENTS_DIR`),
204/// if found
205///
206/// <div class="warning">
207///
208/// This function will parse the `user-dirs.dirs` file every time it's called,
209/// so if you need paths to multiple different directories - consider using
210/// [`UserDirs`] instead
211///
212/// </div>
213pub fn documents() -> Result<Option<PathBuf>, Error> {
214 read_single(utils::DOCUMENTS)
215}
216
217/// Returns an absolute path to users downloads directory (`XDG_DOWNLOAD_DIR`),
218/// if found
219///
220/// <div class="warning">
221///
222/// This function will parse the `user-dirs.dirs` file every time it's called,
223/// so if you need paths to multiple different directories - consider using
224/// [`UserDirs`] instead
225///
226/// </div>
227pub fn downloads() -> Result<Option<PathBuf>, Error> {
228 read_single(utils::DOWNLOADS)
229}
230
231/// Returns an absolute path to users music directory (`XDG_MUSIC_DIR`), if
232/// found
233///
234/// <div class="warning">
235///
236/// This function will parse the `user-dirs.dirs` file every time it's called,
237/// so if you need paths to multiple different directories - consider using
238/// [`UserDirs`] instead
239///
240/// </div>
241pub fn music() -> Result<Option<PathBuf>, Error> {
242 read_single(utils::MUSIC)
243}
244
245/// Returns an absolute path to users pictures directory (`XDG_PICTURES_DIR`),
246/// if found
247///
248/// <div class="warning">
249///
250/// This function will parse the `user-dirs.dirs` file every time it's called,
251/// so if you need paths to multiple different directories - consider using
252/// [`UserDirs`] instead
253///
254/// </div>
255pub fn pictures() -> Result<Option<PathBuf>, Error> {
256 read_single(utils::PICTURES)
257}
258
259/// Returns an absolute path to users public share directory
260/// (`XDG_PUBLICSHARE_DIR`), if found
261///
262/// <div class="warning">
263///
264/// This function will parse the `user-dirs.dirs` file every time it's called,
265/// so if you need paths to multiple different directories - consider using
266/// [`UserDirs`] instead
267///
268/// </div>
269pub fn public() -> Result<Option<PathBuf>, Error> {
270 read_single(utils::PUBLIC)
271}
272
273/// Returns an absolute path to users templates directory (`XDG_TEMPLATES_DIR`),
274/// if found
275///
276/// <div class="warning">
277///
278/// This function will parse the `user-dirs.dirs` file every time it's called,
279/// so if you need paths to multiple different directories - consider using
280/// [`UserDirs`] instead
281///
282/// </div>
283pub fn templates() -> Result<Option<PathBuf>, Error> {
284 read_single(utils::TEMPLATES)
285}
286
287/// Returns an absolute path to users videos directory (`XDG_VIDEOS_DIR`), if
288/// found
289///
290/// <div class="warning">
291///
292/// This function will parse the `user-dirs.dirs` file every time it's called,
293/// so if you need paths to multiple different directories - consider using
294/// [`UserDirs`] instead
295///
296/// </div>
297pub fn videos() -> Result<Option<PathBuf>, Error> {
298 read_single(utils::VIDEOS)
299}
300
301/// Returns an absolute path to users projects directory (`XDG_PROJECTS_DIR`),
302/// if found
303///
304/// <div class="warning">
305///
306/// This function will parse the `user-dirs.dirs` file every time it's called,
307/// so if you need paths to multiple different directories - consider using
308/// [`UserDirs`] instead
309///
310/// </div>
311pub fn projects() -> Result<Option<PathBuf>, Error> {
312 read_single(utils::PROJECTS)
313}