Repository navigation
Add std::fs::{Home|Media}Dirs - #158936
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Reviewed the Darwin parts.
calling those correctly requires an active Objective C autorelease pool, IIUC
Not that hard though, you can push and pop it with objc_autoreleasePoolPush/objc_autoreleasePoolPop. The bigger problem is that it requires linking Foundation, which has a startup cost we'd rather avoid.
This implementation diverges from the directories crate's mapping [...]
It seems to me that for something as nuanced as these user dirs (with a lot of platform-specific details that are not readily apparent), it might make sense to implement the desires std API in directories first? And once it stabilizes more there, we could upstream it to std?
This is mostly already the case. The only API-facing changes from directories here are:
The ideal API shape inside std and in a crate often differ slightly. This approved impl experiment is to determine if a form of this API that fits std's goals exists. I'm going to split the base directory discovery and the user/media directories into different types to better represent that the existence of these sets is not strongly correlated and fix the things @madsmtm pointed out w.r.t. docs and the darwin impl, then this should be good for proper libs-api review. The use of shlex for shell-unquote for the XDG user dirs needs a resolution, but doing the work to give shlex a rustc-dep-of-std feature can wait until we know whether that's the direction we want to take. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I consider this fully ready for review now. r? @rust-lang/libs-api |
Add `std::fs::{Home|Media}Dirs`
- ACP: rust-lang/libs-team#830
Replacement for `std::os::unix::xdg` as suggested by libs-api in rust-lang#157515 (comment). Exposes media directories common between the three big OSes in addition to the cache/config/data/state directories under a separate feature gate. API summary:
```rust
// mod std::fs
pub struct HomeDirs { /* ... */ }
impl HomeDirs {
fn empty() -> Self;
pub fn config_home(&self) -> Option<&Path>;
pub fn data_home(&self) -> Option<&Path>;
pub fn state_home(&self) -> Option<&Path>;
pub fn cache_home(&self) -> Option<&Path>;
pub fn set_config_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_data_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_state_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_cache_home(&mut self, path: PathBuf) -> &mut Self;
}
pub struct MediaDirs { /* ... */ }
impl MediaDirs {
pub fn empty() -> Self;
pub fn desktop(&self) -> Option<&Path>;
pub fn documents(&self) -> Option<&Path>;
pub fn downloads(&self) -> Option<&Path>;
pub fn music(&self) -> Option<&Path>;
pub fn pictures(&self) -> Option<&Path>;
pub fn videos(&self) -> Option<&Path>;
pub fn set_desktop(&mut self, path: PathBuf) -> &mut Self;
pub fn set_documents(&mut self, path: PathBuf) -> &mut Self;
pub fn set_downloads(&mut self, path: PathBuf) -> &mut Self;
pub fn set_music(&mut self, path: PathBuf) -> &mut Self;
pub fn set_pictures(&mut self, path: PathBuf) -> &mut Self;
pub fn set_videos(&mut self, path: PathBuf) -> &mut Self;
}
// mod std::os::darwin::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn sysdir() -> io::Result<Self>;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn sysdir() -> io::Result<Self>;
}
// mod std::os::unix::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn xdg() -> io::Result<Self>;
fn runtime_home(&self) -> Option<&Path>;
fn config_dirs(&self) -> Option<XdgDirs<'_>>;
fn data_dirs(&self) -> Option<XdgDirs<'_>>;
fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self;
fn set_config_dirs(&mut self, paths: OsString) -> &mut Self;
fn set_data_dirs(&mut self, paths: OsString) -> &mut Self;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn xdg() -> io::Result<Self>;
fn templates(&self) -> Option<&Path>;
fn set_templates(&mut self, path: PathBuf) -> &mut Self;
}
pub struct XdgDirs<'a> { /* ... */ }
impl Iterator for XdgDirs<'a> {
type Item = &'a Path;
/* ... */
}
// mod std::os::windows::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn appdata_env() -> io::Result<Self>;
fn known_folders() -> io::Result<Self>;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn known_folders() -> io::Result<Self>;
}
```
…uwer Rollup of 5 pull requests Successful merges: - #158102 (When compiling without a specified `--edition`, emit a message) - #158936 (Add `std::fs::{Home|Media}Dirs`) - #163414 (Add support for span context to `derive(Diagnostic)` and use it) - #163405 (Remove some #[linkage] options) - #163498 (Revert "Rollup merge of #120589 - devnexen:cpuaff_fbsd_upd, r=clarfonthey")
Add `std::fs::{Home|Media}Dirs`
- ACP: rust-lang/libs-team#830
Replacement for `std::os::unix::xdg` as suggested by libs-api in rust-lang#157515 (comment). Exposes media directories common between the three big OSes in addition to the cache/config/data/state directories under a separate feature gate. API summary:
```rust
// mod std::fs
pub struct HomeDirs { /* ... */ }
impl HomeDirs {
fn empty() -> Self;
pub fn config_home(&self) -> Option<&Path>;
pub fn data_home(&self) -> Option<&Path>;
pub fn state_home(&self) -> Option<&Path>;
pub fn cache_home(&self) -> Option<&Path>;
pub fn set_config_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_data_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_state_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_cache_home(&mut self, path: PathBuf) -> &mut Self;
}
pub struct MediaDirs { /* ... */ }
impl MediaDirs {
pub fn empty() -> Self;
pub fn desktop(&self) -> Option<&Path>;
pub fn documents(&self) -> Option<&Path>;
pub fn downloads(&self) -> Option<&Path>;
pub fn music(&self) -> Option<&Path>;
pub fn pictures(&self) -> Option<&Path>;
pub fn videos(&self) -> Option<&Path>;
pub fn set_desktop(&mut self, path: PathBuf) -> &mut Self;
pub fn set_documents(&mut self, path: PathBuf) -> &mut Self;
pub fn set_downloads(&mut self, path: PathBuf) -> &mut Self;
pub fn set_music(&mut self, path: PathBuf) -> &mut Self;
pub fn set_pictures(&mut self, path: PathBuf) -> &mut Self;
pub fn set_videos(&mut self, path: PathBuf) -> &mut Self;
}
// mod std::os::darwin::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn sysdir() -> io::Result<Self>;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn sysdir() -> io::Result<Self>;
}
// mod std::os::unix::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn xdg() -> io::Result<Self>;
fn runtime_home(&self) -> Option<&Path>;
fn config_dirs(&self) -> Option<XdgDirs<'_>>;
fn data_dirs(&self) -> Option<XdgDirs<'_>>;
fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self;
fn set_config_dirs(&mut self, paths: OsString) -> &mut Self;
fn set_data_dirs(&mut self, paths: OsString) -> &mut Self;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn xdg() -> io::Result<Self>;
fn templates(&self) -> Option<&Path>;
fn set_templates(&mut self, path: PathBuf) -> &mut Self;
}
pub struct XdgDirs<'a> { /* ... */ }
impl Iterator for XdgDirs<'a> {
type Item = &'a Path;
/* ... */
}
// mod std::os::windows::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn appdata_env() -> io::Result<Self>;
fn known_folders() -> io::Result<Self>;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn known_folders() -> io::Result<Self>;
}
```
Rollup of 8 pull requests Successful merges: - #158102 (When compiling without a specified `--edition`, emit a message) - #158936 (Add `std::fs::{Home|Media}Dirs`) - #163414 (Add support for span context to `derive(Diagnostic)` and use it) - #163481 (triagebot: use r-l/r zulip linkifier for r-l/r rustfmt backport nominations) - #163502 (arch::breakpoint: update docs) - #163505 (Sync cg_gcc subtree 2026-09-29) - #163405 (Remove some #[linkage] options) - #163498 (Revert "Rollup merge of #120589 - devnexen:cpuaff_fbsd_upd, r=clarfonthey")
Rollup of 8 pull requests Successful merges: - #158102 (When compiling without a specified `--edition`, emit a message) - #158936 (Add `std::fs::{Home|Media}Dirs`) - #163414 (Add support for span context to `derive(Diagnostic)` and use it) - #163481 (triagebot: use r-l/r zulip linkifier for r-l/r rustfmt backport nominations) - #163502 (arch::breakpoint: update docs) - #163505 (Sync cg_gcc subtree 2026-09-29) - #163405 (Remove some #[linkage] options) - #163498 (Revert "Rollup merge of #120589 - devnexen:cpuaff_fbsd_upd, r=clarfonthey")
Add `std::fs::{Home|Media}Dirs`
- ACP: rust-lang/libs-team#830
Replacement for `std::os::unix::xdg` as suggested by libs-api in rust-lang#157515 (comment). Exposes media directories common between the three big OSes in addition to the cache/config/data/state directories under a separate feature gate. API summary:
```rust
// mod std::fs
pub struct HomeDirs { /* ... */ }
impl HomeDirs {
fn empty() -> Self;
pub fn config_home(&self) -> Option<&Path>;
pub fn data_home(&self) -> Option<&Path>;
pub fn state_home(&self) -> Option<&Path>;
pub fn cache_home(&self) -> Option<&Path>;
pub fn set_config_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_data_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_state_home(&mut self, path: PathBuf) -> &mut Self;
pub fn set_cache_home(&mut self, path: PathBuf) -> &mut Self;
}
pub struct MediaDirs { /* ... */ }
impl MediaDirs {
pub fn empty() -> Self;
pub fn desktop(&self) -> Option<&Path>;
pub fn documents(&self) -> Option<&Path>;
pub fn downloads(&self) -> Option<&Path>;
pub fn music(&self) -> Option<&Path>;
pub fn pictures(&self) -> Option<&Path>;
pub fn videos(&self) -> Option<&Path>;
pub fn set_desktop(&mut self, path: PathBuf) -> &mut Self;
pub fn set_documents(&mut self, path: PathBuf) -> &mut Self;
pub fn set_downloads(&mut self, path: PathBuf) -> &mut Self;
pub fn set_music(&mut self, path: PathBuf) -> &mut Self;
pub fn set_pictures(&mut self, path: PathBuf) -> &mut Self;
pub fn set_videos(&mut self, path: PathBuf) -> &mut Self;
}
// mod std::os::darwin::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn sysdir() -> io::Result<Self>;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn sysdir() -> io::Result<Self>;
}
// mod std::os::unix::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn xdg() -> io::Result<Self>;
fn runtime_home(&self) -> Option<&Path>;
fn config_dirs(&self) -> Option<XdgDirs<'_>>;
fn data_dirs(&self) -> Option<XdgDirs<'_>>;
fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self;
fn set_config_dirs(&mut self, paths: OsString) -> &mut Self;
fn set_data_dirs(&mut self, paths: OsString) -> &mut Self;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn xdg() -> io::Result<Self>;
fn templates(&self) -> Option<&Path>;
fn set_templates(&mut self, path: PathBuf) -> &mut Self;
}
pub struct XdgDirs<'a> { /* ... */ }
impl Iterator for XdgDirs<'a> {
type Item = &'a Path;
/* ... */
}
// mod std::os::windows::fs
impl HomeDirsExt for HomeDirs { /* ... */ }
pub impl(self) trait HomeDirsExt {
fn appdata_env() -> io::Result<Self>;
fn known_folders() -> io::Result<Self>;
}
impl MediaDirsExt for MediaDirs { /* ... */ }
pub impl(self) trait MediaDirsExt {
fn known_folders() -> io::Result<Self>;
}
```
Rollup of 7 pull requests Successful merges: - #158936 (Add `std::fs::{Home|Media}Dirs`) - #163414 (Add support for span context to `derive(Diagnostic)` and use it) - #163481 (triagebot: use r-l/r zulip linkifier for r-l/r rustfmt backport nominations) - #163502 (arch::breakpoint: update docs) - #163505 (Sync cg_gcc subtree 2026-09-29) - #163405 (Remove some #[linkage] options) - #163498 (Revert "Rollup merge of #120589 - devnexen:cpuaff_fbsd_upd, r=clarfonthey")
Rollup of 6 pull requests Successful merges: - #158936 (Add `std::fs::{Home|Media}Dirs`) - #163414 (Add support for span context to `derive(Diagnostic)` and use it) - #163481 (triagebot: use r-l/r zulip linkifier for r-l/r rustfmt backport nominations) - #163502 (arch::breakpoint: update docs) - #163505 (Sync cg_gcc subtree 2026-09-29) - #163498 (Revert "Rollup merge of #120589 - devnexen:cpuaff_fbsd_upd, r=clarfonthey")
|
Note This PR was benchmarked as part of triage of its containing rollup: triage URL. Finished benchmarking commit (f58b305): comparison URL. Overall result: ❌ regressions - please read:Our benchmarks found a performance regression caused by this PR. Next Steps:
@rustbot label: +perf-regression Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (secondary -2.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesThis perf run didn't have relevant results for this metric. Binary sizeResults (primary 1.2%, secondary 1.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: missing data |
|
This PR caused a significant perf regression, this seems to be unexpected. |
|
If the regressions aren't spurious, it's probably due to the new |
|
From looking at the profiling results, the extra cost seems to be concentrated in |
|
I'm completely lost as to how this could have caused the regression; I'll leave figuring that out to the perf WG. Though I will submit a quick PR to remove that spurious cfg_select! arm. |
|
Before this PR, |
|
Opened #163569 to test the hypothesis of Random thought: on Windows, this newly links to |
| #[cfg(test)] | ||
| mod tests { |
There was a problem hiding this comment.
Isn't tidy supposed to reject same-file mod tests?
|
This definitely looks like
|
Did you see if it was the docs? (the doc comments should still be stored in the rmeta, I don't believe we managed to remove them yet). |
View all comments
std::fs::UserDirsor similar libs-team#830Replacement for
std::os::unix::xdgas suggested by libs-api in #157515 (comment). Exposes media directories common between the three big OSes in addition to the cache/config/data/state directories under a separate feature gate. API summary:Potentially outdated info
This implementation diverges from the
directoriescrate's mapping in that we setstate_dirin the non-unix constructors (to~/Library/Application Supporton Darwin and%APPDATA%on Windows). This mapping is derived from the idea that "state" files are application support files that are not important nor portable enough to the user to be "data" files.The XDG paths are as described in the XDG Base Directories Specification and the xdg-user-dirs tool.
$XDG_CONFIG_DIR/user-dirs.dirsis parsed directly to avoid delegating to potentially arbitrary shell execution.The Darwin paths are loaded via the
sysdir(3)API fromlibSystem.dylib(introduced in macOS 10.12 with a similar timeline for other Darwin OSes, deprecating the earlierNSSystemDirectories.hAPI). Using the File System Effectively points to preferring the Foundation framework'sNSSearchPathForDirectoriesInDomain(_:_:_:)orNSFileManager.URLForDirectoryinstead, but calling those correctly requires an active Objective C autorelease pool, IIUC. TheLibrary/Application Supportdirectory is used forconfig_home,data_home, andstate_home; the Apple documentationThe Library Directory Stores App-Specific Filesdirectly calls out placing data and configuration files inLibrary/Application Support, and state files are just less user-meaningful data files.The Windows paths are loaded via the Known Folders API (introduced in Vista).
config_homeanddata_homeare placed inAppData\Roamingas files intended to be important and portable to the user, whilecache_homeandstate_homeare placed inAppData\Localas files that aren't.I'm not fully confident about the handling of the XDG base directory paths which don't have good cross-platform analogs, as well as the exact API for the search path dealing functions, but I'm confident that the shape of the rest of the API does match the stdlib API style. Common paths are platform-independent enough of a needed concept to be exposed by std, IMHO, but platform-specific that a struct with public fields (even
#[non_exhaustive]) seems incorrect, specifically because of platform-specific paths that we may want to expose like is already done for XDG.The one API change I could see doing is moving
state_dirinto the XDGUserDirsExt. I chose not to do this for this initial implementation, though, as getting the ideal choice of fallback for both Darwin and Windows can't be achieved in an OS-agnostic way:~/Library/Caches~/AppData/Local~/Library/Application Support~/AppData/Roaming~/Library/Application Support~/AppData/Roaming~/Library/Application Support~/AppData/LocalA more drastic change would be to move all four onto the unix
UserDirsExt, addingcaches/application_supportto the DarwinUserDirsExtandroaming_app_data/local_app_datato the WindowsUserDirsExt. This would be more "correct" but seems a bit heavy-handed, as it would mean applications need to pull in OS-specific extension traits just to place their support files in something more appropriate than a~/.appnamedirectory.We could also separate the "home" directory API from the "media" directory API. I'm neutral on this with one relevant note: the app-specific cache/config/data/state files need a subdirectory named after the application, so "
ProjectDirs" would exclude the media directories; it could make sense to have a type with just those and apush_application_subdirmethod.Switching the impl to using a pal
imp::UserDirscould be reasonable, but seems at odds with the desire to have the target agnostic way to "build your own"UserDirs. AnExtraUserDirsinstead of the#[allow(dead_code)]fields would make sense, I just didn't know how to best set up that in the pal layer.Disclaimer: This was worked on as part of my employment at Canonical. I initially proposed it independently of my employment, but improving std's functionality is part of my job description, so Canonical told me I should use work time on it.
AI Disclosure: I did not use AI to generate any of the code, with a partial exception for VSCode's AI-assisted smart autocomplete helping with the repetitive parts of the code. All nontrivial code was handwritten. As an experiment, I did use some AI to assist in exploring the problem and API design spaces.
I tested locally on my Ubuntu developer machine, but am relying on CI for Darwin and Windows tests. 🤞
cc @joshtriplett @nia-e