Skip to main content

nifty_runtime/
paths.rs

1//! Paths as apps write them, shared by downloads and file dialogs.
2
3use std::fmt;
4use std::path::PathBuf;
5
6/// `path` with a leading `~` standing for the user's home folder.
7///
8/// # Errors
9/// When `path` starts with `~` and the home folder can't be found.
10pub fn expand_home(path: &str) -> Result<PathBuf, NoHomeFolder> {
11    let Some(rest) = path.strip_prefix('~') else {
12        return Ok(PathBuf::from(path));
13    };
14    if !rest.is_empty() && !rest.starts_with(std::path::is_separator) {
15        return Ok(PathBuf::from(path));
16    }
17    let home = std::env::home_dir().ok_or(NoHomeFolder)?;
18    Ok(home.join(rest.trim_start_matches(std::path::is_separator)))
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct NoHomeFolder;
23
24impl fmt::Display for NoHomeFolder {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.write_str("couldn't find your home folder for `~`")
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn a_leading_tilde_means_the_home_folder() {
36        let home = std::env::home_dir().expect("tests run with a home folder");
37        assert_eq!(expand_home("~").ok(), Some(home.clone()));
38        assert_eq!(
39            expand_home("~/Downloads").ok(),
40            Some(home.join("Downloads"))
41        );
42        assert_eq!(expand_home("~other").ok(), Some(PathBuf::from("~other")));
43        assert_eq!(expand_home("/tmp").ok(), Some(PathBuf::from("/tmp")));
44    }
45}