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,
xdgdirstays 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.
xdgdiris 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,
xdgdirworks everywhere. You can use it in anasyncfunction 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.