In several of my personal projects I have a need for tilde expansion, which means taking a filename such as ~/foo and expanding the tilde into the current user’s home directory. For me, that would result in /home/liw/foo. This is ubiquitous in Unix, and now unknown elsewhere. The usual tilde syntax is a little more complex than that: one can refer to another user’s home directory as well.

input output
~ /home/liw
~/foo /home/liw/foo
~tomjon /home/tomjon
~tomjon/foo /home/tomjon/foo

I want to support tilde expansion in places such as configuration files. In such places can’t rely on the shell to do the expansion for me, like I can for command line arguments. Thus, I would like a Rust crate to do this for me.

I have some requirements:

  • The crate MUST support any valid filename on the system. It’s NOT OK to require the input filename to be UTF8 encoded. In practice this probably means the input should be a &Path, or AsRef<Path>. However, having variants that take a Rust string as input is OK.
  • The result MUST be a Path or PathBuf. I’m NOT OK with getting a string, or even an array of bytes. It’s not ergonomic to make the caller convert the result to a standard filename representation. I’m not even willing to write a wrapper to do the conversion: I want to be lazy.
  • The crate MUST report errors, especially for an input that refers to a non-existent user. I want my programs to be able to produce excellent error messages, and to not fail in ways the confuse the user.
  • The crate MUST have a license that’s compatible with my code. I tend to favor strong copyleft licenses, such as the GPL.

I found a bunch of crates to evaluate. Here are results.

  • untildify
    • deals with UTF8 strings only only, making this unacceptable to me
    • uses the WTFPL license, which is also unacceptable to me
  • tilde-expand
    • deals with byte strings, not Path or PathBuf
    • does not handle errors in a useful way
    • gets unknown user wrong on my system (/var/lib/nfs)
  • shellexpand
    • currently unmaintained
    • also does shell variable expansion but has function for just tilde expansion so that’s OK
    • input and output are both UTF8 strings
    • doesn’t handled ~liw as input, returning ~liw
    • does not give good error for unknown user, returns the input string in that case
  • home-dir
    • lacks README on crates.io
    • defines a trait that adds an expand_home to types that implement the trait
    • implements the trait for Path and &Path, so that’s acceptable
    • handles errors, though the error message for a non-existent user could be improved
    • documentation could be better: for example, it lacks an example on front page
  • expanduser
    • lacks README on crates.io
    • takes a UTF8 string as input, which is not good enough for me
    • return a result, using std::io::ErrorKind::Other to indicate a non-existent user, which I could live with, but could be improved

Of these, I’m leaning towards using the home-dir crate. I’ll offer changes to improve its documentation and other aspects of it I wish were better.