Why It Never Touches Your Filesystem
My thinking behind making this a pure path resolver with zero filesystem calls.
When I designed xdgdir
, I made one very important rule for myself: it must
never perform any I/O. This means it doesn’t read files, write files, create
directories, or even check if a path exists. It is a pure path resolver, and
nothing more.
This was a very intentional choice. Here’s my thinking behind it.
Why I Made This Choice
- It’s Fast. Filesystem operations can be slow. Even just checking if a
directory exists adds overhead. By sticking to path manipulation,
xdgdir
stays super fast. It’s just joining strings together based on some simple rules. - It’s Predictable. A function that talks to the filesystem can fail for
many reasons like permissions errors.
xdgdir
is totally predictable. Given the same environment variables, it will always give you the samePathBuf
. This makes your code easier to test and reason about. - It’s Flexible. Because it doesn’t do I/O,
xdgdir
works everywhere. You can use it in anasync
function without worrying about blocking the runtime. You can use it in a sandboxed environment where filesystem access might be restricted. The library doesn’t care.
What This Means For You
The key takeaway is that xdgdir
only tells you where a directory should be.
It does not guarantee that the directory exists.
It is your application’s job to handle the filesystem part. For example, before
you write a config file, you should first make sure its parent directory exists.
The easiest way to do this is with std::fs::create_dir_all
.
use std::fs;
use xdgdir::BaseDir;
fn main() {
let dirs = BaseDir::new("my-app").unwrap();
// This will create the config directory if it doesn't exist.
// If it already exists, it does nothing.
fs::create_dir_all(&dirs.config).unwrap();
let config_file_path = dirs.config.join("settings.toml");
fs::write(config_file_path, "theme = 'dark'").unwrap();
}
This separation of concerns is a feature. xdgdir
does one thing well: it
resolves paths according to the spec. Your code then takes those paths and uses
them however it needs to. I’m using .unwrap()
in the example to keep it
focused, but in a real app, you would want to handle these potential I/O errors.