diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index f5c60f7562d4a..6e401113cb26e 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -502,6 +502,57 @@ impl fmt::Debug for SplitPaths<'_> {
}
}
+/// An iterator that splits an environment variable into paths according to
+/// platform-specific conventions.
+///
+/// The iterator element type is &[Path].
+///
+/// This structure is created by [`env::split_paths_ref()`]. See its
+/// documentation for more.
+///
+/// [`env::split_paths_ref()`]: split_paths_ref
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+#[unstable(feature = "env_split_paths_ref", issue = "none")]
+pub struct SplitPathsRef<'a> {
+ inner: paths_imp::SplitPathsRef<'a>,
+}
+
+/// Parses input according to platform conventions for the `PATH`
+/// environment variable.
+///
+/// Unlike [`split_paths`], this function does not allocate and instead yields
+/// [`Path`]s borrowed from `unparsed`. Returns `None` on platforms that may
+/// require allocations to handle `PATH` splitting conventions.
+///
+/// # Platform-specific behavior
+///
+/// Returns `Some` on Unix platforms and `None` on all other platforms.
+/// Note that this [may change in the future][changes].
+///
+/// [changes]: io#platform-specific-behavior
+#[unstable(feature = "env_split_paths_ref", issue = "none")]
+pub fn split_paths_ref + ?Sized>(unparsed: &T) -> Option> {
+ Some(SplitPathsRef { inner: paths_imp::split_paths_ref(unparsed.as_ref())? })
+}
+
+#[unstable(feature = "env_split_paths_ref", issue = "none")]
+impl<'a> Iterator for SplitPathsRef<'a> {
+ type Item = &'a Path;
+ fn next(&mut self) -> Option<&'a Path> {
+ self.inner.next()
+ }
+ fn size_hint(&self) -> (usize, Option) {
+ self.inner.size_hint()
+ }
+}
+
+#[unstable(feature = "env_split_paths_ref", issue = "none")]
+impl fmt::Debug for SplitPathsRef<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SplitPathsRef").finish_non_exhaustive()
+ }
+}
+
/// The error type for operations on the `PATH` variable. Possibly returned from
/// [`env::join_paths()`].
///
@@ -606,7 +657,6 @@ impl Error for JoinPathsError {
/// For example, [XDG Base Directories] on Unix or the `LOCALAPPDATA` and `APPDATA` environment variables on Windows.
///
/// [XDG Base Directories]: https://specifications.freedesktop.org/basedir-spec/latest/
-// feature(xdg_basedir): This should link to std::os::unix::xdg once it's stabilized
///
/// # Unix
///
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 8df809264a6dd..3a4f14c3306bc 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -49,6 +49,13 @@ use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp};
use crate::time::SystemTime;
use crate::{error, fmt};
+pub(crate) mod dirs;
+
+#[unstable(feature = "fs_home_dirs", issue = "162082")]
+pub use self::dirs::HomeDirs;
+#[unstable(feature = "fs_media_dirs", issue = "162083")]
+pub use self::dirs::MediaDirs;
+
/// An object providing access to an open file on the filesystem.
///
/// An instance of a `File` can be read and/or written depending on what options
diff --git a/library/std/src/fs/dirs.rs b/library/std/src/fs/dirs.rs
new file mode 100644
index 0000000000000..edbaeaa16e2e5
--- /dev/null
+++ b/library/std/src/fs/dirs.rs
@@ -0,0 +1,572 @@
+use crate::path::{Path, PathBuf};
+use crate::sys::fs::{ExtraHomeDirs, ExtraMediaDirs};
+
+/// Common user directory paths used for user-specific application files.
+///
+/// It is not required that the user directories are accessible by the current
+/// user, nor that there is a directory at that path. A robust application
+/// should handle the case where user directories are incorrectly configured.
+///
+/// Even when configured correctly, multiple paths may be to the same location.
+/// As such, you should not assume that a file written relative to one directory
+/// will not conflict with the same relative path in a different home directory.
+///
+/// # Platform-specific behavior
+///
+/// As the filesystem conventions for discovering directories varies between
+/// operating systems, constructors for `HomeDirs` that use the host platform's
+/// conventions are provided as extension traits under the `std::os` module.
+#[unstable(feature = "fs_home_dirs", issue = "162082")]
+#[derive(Debug, Clone)]
+pub struct HomeDirs {
+ pub(crate) cache: Option,
+ pub(crate) config: Option,
+ pub(crate) data: Option,
+ pub(crate) state: Option,
+ #[cfg_attr(not(unix), expect(dead_code, reason = "no extra home dirs"))]
+ pub(crate) extra: ExtraHomeDirs,
+}
+
+/// Common user directory paths used for user-specific media files.
+///
+/// It is not required that the media directories are accessible by the current
+/// user, nor that there is a directory at that path. A robust application
+/// should handle the case where media directories are incorrectly configured.
+///
+/// Even when configured correctly, multiple paths may be to the same location.
+/// As such, you should not assume that a file written relative to one directory
+/// will not conflict with the same relative path in a different media directory.
+///
+/// # Platform-specific behavior
+///
+/// As the filesystem conventions for discovering directories varies between
+/// operating systems, constructors for `MediaDirs` that use the host platform's
+/// conventions are provided as extension traits under the `std::os` module.
+#[unstable(feature = "fs_media_dirs", issue = "162083")]
+#[derive(Debug, Clone)]
+pub struct MediaDirs {
+ pub(crate) desktop: Option,
+ pub(crate) documents: Option,
+ pub(crate) downloads: Option,
+ pub(crate) music: Option,
+ pub(crate) pictures: Option,
+ pub(crate) videos: Option,
+ #[cfg_attr(not(unix), expect(dead_code, reason = "no extra media dirs"))]
+ pub(crate) extra: ExtraMediaDirs,
+}
+
+// NB: HomeDirs and MediaDirs intentionally do not implement Default. Self::empty()
+// is a logical default, but users may also intuit the default to use default
+// platform conventions. Omitting Default pushes users to explicitly choose.
+
+impl HomeDirs {
+ /// Create a known user directory set with no known directories.
+ ///
+ /// This is useful with the builder `set_*` methods to create a `HomeDirs`
+ /// with exactly the directories you want, without any other defaults.
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn empty() -> Self {
+ Self { cache: None, config: None, data: None, state: None, extra: Default::default() }
+ }
+
+ /// A base directory relative to which user-specific non-essential cache
+ /// data files should be stored.
+ ///
+ /// "Cache" files are temporary data that can be used to cache redundant
+ /// work of an application, but which can be discarded arbitrarily and
+ /// recreated as necessary. Files in this directory may potentially be
+ /// automatically purged any time they are not currently open, or they
+ /// may not, depending on system configuration. A robust application
+ /// should ensure that its caches do not grow without a reasonable bound.
+ ///
+ /// This is the same directory for all applications. As such, applications
+ /// should use a subdirectory for application-specific cache files.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `${XDG_CACHE_HOME:-$HOME/.cache}` |
+ /// | [Darwin] (macOS) | [`NSCachesDirectory`] (`$HOME/Library/Caches`) |
+ /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) |
+ ///
+ /// Other paths can be configured via [`set_cache_home`](Self::set_cache_home).
+ ///
+ /// [XDG]: crate::os::unix::fs::HomeDirsExt
+ /// [Darwin]: crate::os::darwin::fs::HomeDirsExt
+ /// [Windows]: crate::os::windows::fs::HomeDirsExt
+ /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc
+ /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn cache_home(&self) -> Option<&Path> {
+ self.cache.as_deref()
+ }
+
+ /// A base directory relative to which user-specific configuration files
+ /// should be stored.
+ ///
+ /// "Config" files are configuration managed by the user, either by editing
+ /// the files directly or through a managing application. Configuration is
+ /// generally expected to be meaningful to the user and portable enough to
+ /// back up and synchronize across the same user's account on multiple
+ /// systems.
+ ///
+ /// This is the same directory for all applications. As such, applications
+ /// should use a subdirectory for application-specific configuration files.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `${XDG_CONFIG_HOME:-$HOME/.config}` |
+ /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) |
+ /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) |
+ ///
+ /// Other paths can be configured via [`set_config_home`](Self::set_config_home).
+ ///
+ /// [XDG]: crate::os::unix::fs::HomeDirsExt
+ /// [Darwin]: crate::os::darwin::fs::HomeDirsExt
+ /// [Windows]: crate::os::windows::fs::HomeDirsExt
+ /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc
+ /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn config_home(&self) -> Option<&Path> {
+ self.config.as_deref()
+ }
+
+ /// A base directory relative to which user-specific data files should be
+ /// stored.
+ ///
+ /// "Data" files are application-specific data that is meaningful to the
+ /// user in some way and does not implicitly rely on system configuration
+ /// or details of how the application is installed otherwise irrelevant to
+ /// the user. As such, data makes sense to back up and synchronize between
+ /// the same user's account on multiple systems. If a file is specific to
+ /// a single machine, it's probably [state](Self::state_home).
+ ///
+ /// This is the same directory for all applications. As such, applications
+ /// should use a subdirectory for application-specific data files.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `${XDG_DATA_HOME:-$HOME/.local/share}` |
+ /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) |
+ /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) |
+ ///
+ /// Other paths can be configured via [`set_data_home`](Self::set_data_home).
+ ///
+ /// [XDG]: crate::os::unix::fs::HomeDirsExt
+ /// [Darwin]: crate::os::darwin::fs::HomeDirsExt
+ /// [Windows]: crate::os::windows::fs::HomeDirsExt
+ /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc
+ /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn data_home(&self) -> Option<&Path> {
+ self.data.as_deref()
+ }
+
+ /// A base directory relative to which user-specific state files should be
+ /// stored.
+ ///
+ /// "State" files are data that should persist between application restarts,
+ /// but which is not important nor portable enough to the user to synchronize
+ /// between multiple systems like [data](Self::data_home) are. Common examples
+ /// include history (such as logs, recently used files, etc) and any current
+ /// state of the application that should be reused (such as view, layout, open
+ /// files, undo history, etc).
+ ///
+ /// This is the same directory for all applications. As such, applications
+ /// should use a subdirectory for application-specific state files.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `${XDG_STATE_HOME:-$HOME/.local/state}` |
+ /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) |
+ /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) |
+ ///
+ /// Other paths can be configured via [`set_state_home`](Self::set_state_home).
+ ///
+ /// [XDG]: crate::os::unix::fs::HomeDirsExt
+ /// [Darwin]: crate::os::darwin::fs::HomeDirsExt
+ /// [Windows]: crate::os::windows::fs::HomeDirsExt
+ /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc
+ /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn state_home(&self) -> Option<&Path> {
+ self.state.as_deref()
+ }
+}
+
+impl MediaDirs {
+ /// Create a known user directory set with no known directories.
+ ///
+ /// This is useful with the builder `set_*` methods to create a `MediaDirs`
+ /// with exactly the directories you want, without any other defaults.
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn empty() -> Self {
+ Self {
+ desktop: None,
+ documents: None,
+ downloads: None,
+ music: None,
+ pictures: None,
+ videos: None,
+ extra: Default::default(),
+ }
+ }
+
+ /// The OS-recognized user "Desktop" directory, often the `Desktop`
+ /// folder in the user's home directory.
+ ///
+ /// As a media directory, this should typically be used as a default path
+ /// for file selection dialogs, not for automatically accessed file paths.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `xdg-user-dir DESKTOP` (`$HOME/Desktop`) |
+ /// | [Darwin] (macOS) | [`NSDesktopDirectory`] (`$HOME/Desktop`) |
+ /// | [Windows] | [`{FOLDERID_Desktop}`] (`%USERPROFILE%\Desktop`) |
+ ///
+ /// Other paths can be configured via [`set_desktop`](Self::set_desktop).
+ ///
+ /// [XDG]: crate::os::unix::fs::MediaDirsExt
+ /// [Darwin]: crate::os::darwin::fs::MediaDirsExt
+ /// [Windows]: crate::os::windows::fs::MediaDirsExt
+ /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc
+ /// [`{FOLDERID_Desktop}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_desktop
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn desktop(&self) -> Option<&Path> {
+ self.desktop.as_deref()
+ }
+
+ /// The OS-recognized user "Documents" directory, often the `Documents`
+ /// folder in the user's home directory.
+ ///
+ /// As a media directory, this should typically be used as a default path
+ /// for file selection dialogs, not for automatically accessed file paths.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `xdg-user-dir DOCUMENTS` (`$HOME/Documents`) |
+ /// | [Darwin] (macOS) | [`NSDocumentDirectory`] (`$HOME/Documents`) |
+ /// | [Windows] | [`{FOLDERID_Documents}`] (`%USERPROFILE%\Documents`) |
+ ///
+ /// Other paths can be configured via [`set_documents`](Self::set_documents).
+ ///
+ /// [XDG]: crate::os::unix::fs::MediaDirsExt
+ /// [Darwin]: crate::os::darwin::fs::MediaDirsExt
+ /// [Windows]: crate::os::windows::fs::MediaDirsExt
+ /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc
+ /// [`{FOLDERID_Documents}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_documents
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn documents(&self) -> Option<&Path> {
+ self.documents.as_deref()
+ }
+
+ /// The OS-recognized user "Downloads" directory, often the `Downloads`
+ /// folder in the user's home directory.
+ ///
+ /// As a media directory, this should typically be used as a default path
+ /// for file selection dialogs, not for automatically accessed file paths.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `xdg-user-dir DOWNLOAD` (`$HOME/Downloads`) |
+ /// | [Darwin] (macOS) | [`NSDownloadsDirectory`] (`$HOME/Downloads`) |
+ /// | [Windows] | [`{FOLDERID_Downloads}`] (`%USERPROFILE%\Downloads`) |
+ ///
+ /// Other paths can be configured via [`set_downloads`](Self::set_downloads).
+ ///
+ /// [XDG]: crate::os::unix::fs::MediaDirsExt
+ /// [Darwin]: crate::os::darwin::fs::MediaDirsExt
+ /// [Windows]: crate::os::windows::fs::MediaDirsExt
+ /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc
+ /// [`{FOLDERID_Downloads}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_downloads
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn downloads(&self) -> Option<&Path> {
+ self.downloads.as_deref()
+ }
+
+ /// The OS-recognized user "Music" directory, often the `Music`
+ /// folder in the user's home directory.
+ ///
+ /// As a media directory, this should typically be used as a default path
+ /// for file selection dialogs, not for automatically accessed file paths.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `xdg-user-dir MUSIC` (`$HOME/Music`) |
+ /// | [Darwin] (macOS) | [`NSMusicDirectory`] (`$HOME/Music`) |
+ /// | [Windows] | [`{FOLDERID_Music}`] (`%USERPROFILE%\Music`) |
+ ///
+ /// Other paths can be configured via [`set_music`](Self::set_music).
+ ///
+ /// [XDG]: crate::os::unix::fs::MediaDirsExt
+ /// [Darwin]: crate::os::darwin::fs::MediaDirsExt
+ /// [Windows]: crate::os::windows::fs::MediaDirsExt
+ /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc
+ /// [`{FOLDERID_Music}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_music
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn music(&self) -> Option<&Path> {
+ self.music.as_deref()
+ }
+
+ /// The OS-recognized user "Pictures" directory, often the `Pictures`
+ /// folder in the user's home directory.
+ ///
+ /// As a media directory, this should typically be used as a default path
+ /// for file selection dialogs, not for automatically accessed file paths.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `xdg-user-dir PICTURES` (`$HOME/Pictures`) |
+ /// | [Darwin] (macOS) | [`NSPicturesDirectory`] (`$HOME/Pictures`) |
+ /// | [Windows] | [`{FOLDERID_Pictures}`] (`%USERPROFILE%\Pictures`) |
+ ///
+ /// Other paths can be configured via [`set_pictures`](Self::set_pictures).
+ ///
+ /// [XDG]: crate::os::unix::fs::MediaDirsExt
+ /// [Darwin]: crate::os::darwin::fs::MediaDirsExt
+ /// [Windows]: crate::os::windows::fs::MediaDirsExt
+ /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc
+ /// [`{FOLDERID_Pictures}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_pictures
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn pictures(&self) -> Option<&Path> {
+ self.pictures.as_deref()
+ }
+
+ /// The OS-recognized user "Videos" directory, often the `Videos`
+ /// folder in the user's home directory.
+ ///
+ /// As a media directory, this should typically be used as a default path
+ /// for file selection dialogs, not for automatically accessed file paths.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// When constructed using platform-specific conventions, the value is:
+ ///
+ /// | OS | Path |
+ /// | -- | ---- |
+ /// | [XDG] (Linux) | `xdg-user-dir VIDEOS` (`$HOME/Videos`) |
+ /// | [Darwin] (macOS) | [`NSMoviesDirectory`] (`$HOME/Movies`) |
+ /// | [Windows] | [`{FOLDERID_Videos}`] (`%USERPROFILE%\Videos`) |
+ ///
+ /// Other paths can be configured via [`set_videos`](Self::set_videos).
+ ///
+ /// [XDG]: crate::os::unix::fs::MediaDirsExt
+ /// [Darwin]: crate::os::darwin::fs::MediaDirsExt
+ /// [Windows]: crate::os::windows::fs::MediaDirsExt
+ /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc
+ /// [`{FOLDERID_Videos}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_videos
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn videos(&self) -> Option<&Path> {
+ self.videos.as_deref()
+ }
+}
+
+impl HomeDirs {
+ /// Set the path for [Self::cache_home].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn set_cache_home(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "cache home directory path must be absolute");
+ self.cache = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::config_home].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn set_config_home(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "config home directory path must be absolute");
+ self.config = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::data_home].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn set_data_home(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "data home directory path must be absolute");
+ self.data = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::state_home].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ pub fn set_state_home(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "state home directory path must be absolute");
+ self.state = Some(path);
+ self
+ }
+}
+
+impl MediaDirs {
+ /// Set the path for [Self::desktop].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn set_desktop(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "desktop directory path must be absolute");
+ self.desktop = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::documents].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn set_documents(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "documents directory path must be absolute");
+ self.documents = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::downloads].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn set_downloads(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "downloads directory path must be absolute");
+ self.downloads = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::music].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn set_music(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "music directory path must be absolute");
+ self.music = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::pictures].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn set_pictures(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "pictures directory path must be absolute");
+ self.pictures = Some(path);
+ self
+ }
+
+ /// Set the path for [Self::videos].
+ ///
+ /// # Panics
+ ///
+ /// Panics if the provided path is not absolute.
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ pub fn set_videos(&mut self, path: PathBuf) -> &mut Self {
+ assert!(path.is_absolute(), "videos directory path must be absolute");
+ self.videos = Some(path);
+ self
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_home_dirs_field_hookup_matches() {
+ let mut dirs = HomeDirs::empty();
+
+ assert_eq!(dirs.config_home(), None);
+ assert_eq!(dirs.data_home(), None);
+ assert_eq!(dirs.state_home(), None);
+ assert_eq!(dirs.cache_home(), None);
+
+ dirs.set_config_home("/config".into());
+ dirs.set_data_home("/data".into());
+ dirs.set_state_home("/state".into());
+ dirs.set_cache_home("/cache".into());
+
+ assert_eq!(dirs.config_home(), Some("/config".as_ref()));
+ assert_eq!(dirs.data_home(), Some("/data".as_ref()));
+ assert_eq!(dirs.state_home(), Some("/state".as_ref()));
+ assert_eq!(dirs.cache_home(), Some("/cache".as_ref()));
+ }
+
+ #[test]
+ fn test_media_dirs_field_hookup_matches() {
+ let mut dirs = MediaDirs::empty();
+
+ assert_eq!(dirs.desktop(), None);
+ assert_eq!(dirs.documents(), None);
+ assert_eq!(dirs.downloads(), None);
+ assert_eq!(dirs.music(), None);
+ assert_eq!(dirs.pictures(), None);
+ assert_eq!(dirs.videos(), None);
+
+ dirs.set_desktop("/desktop".into());
+ dirs.set_documents("/documents".into());
+ dirs.set_downloads("/downloads".into());
+ dirs.set_music("/music".into());
+ dirs.set_pictures("/pictures".into());
+ dirs.set_videos("/videos".into());
+
+ assert_eq!(dirs.desktop(), Some("/desktop".as_ref()));
+ assert_eq!(dirs.documents(), Some("/documents".as_ref()));
+ assert_eq!(dirs.downloads(), Some("/downloads".as_ref()));
+ assert_eq!(dirs.music(), Some("/music".as_ref()));
+ assert_eq!(dirs.pictures(), Some("/pictures".as_ref()));
+ assert_eq!(dirs.videos(), Some("/videos".as_ref()));
+ }
+}
diff --git a/library/std/src/os/darwin/fs.rs b/library/std/src/os/darwin/fs.rs
index fc2869d6b13f6..b41391f19dccf 100644
--- a/library/std/src/os/darwin/fs.rs
+++ b/library/std/src/os/darwin/fs.rs
@@ -7,6 +7,13 @@ use crate::fs::{self, Metadata};
use crate::sys::{AsInner, AsInnerMut, IntoInner};
use crate::time::SystemTime;
+mod dirs;
+
+#[unstable(feature = "fs_home_dirs", issue = "162082")]
+pub use dirs::HomeDirsExt;
+#[unstable(feature = "fs_media_dirs", issue = "162083")]
+pub use dirs::MediaDirsExt;
+
/// OS-specific extensions to [`fs::Metadata`].
///
/// [`fs::Metadata`]: crate::fs::Metadata
diff --git a/library/std/src/os/darwin/fs/dirs.rs b/library/std/src/os/darwin/fs/dirs.rs
new file mode 100644
index 0000000000000..2c0b87cb6aaf1
--- /dev/null
+++ b/library/std/src/os/darwin/fs/dirs.rs
@@ -0,0 +1,313 @@
+use crate::env;
+use crate::fs::{HomeDirs, MediaDirs};
+use crate::io::{self, ErrorKind, const_error};
+use crate::path::PathBuf;
+
+/// Darwin-specific extensions to [`fs::HomeDirs`](HomeDirs).
+#[unstable(feature = "fs_home_dirs", issue = "162082")]
+pub impl(self) trait HomeDirsExt: Sized {
+ /// Load the standard user directory paths for the current user.
+ ///
+ /// On iOS, tvOS, watchOS, visionOS, and sandboxed macOS applications,
+ /// these directories are within the application's container. Outside
+ /// the sandbox, these are subdirectories of the `~/Library` directory on
+ /// macOS.
+ ///
+ /// The produced directory paths are not guaranteed to be the canonical
+ /// paths to the directories; they are allowed to be sandbox-redirected
+ /// paths as long as the directory is accessible there.
+ ///
+ /// The loaded common directories are:
+ ///
+ /// | `HomeDirs` | [`NSSearchPathDirectory`] |
+ /// | ---------- | ----------------------- |
+ /// | [`cache_home`] | [`NSCachesDirectory`] (`~/Library/Caches`) |
+ /// | [`config_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) |
+ /// | [`data_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) |
+ /// | [`state_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) |
+ ///
+ /// Note that the Application Support directory is used for the config,
+ /// data, and state directories. It is always possible for multiple user
+ /// directories to be configured to the same path, but this is the common
+ /// configuration on Apple platforms, making it even more important to not
+ /// assume files in different user directories cannot alias each other.
+ ///
+ /// # Errors
+ ///
+ /// Errors if the [user home](env::home_dir) cannot be determined.
+ // Errors due to the underlying sysdir(3) API should never occur, as
+ // - the user domain only has one directory for each search path;
+ // - the user domain always returns subdirectory paths of `~`; and
+ // - the username and OS defined path segments are always valid UTF-8.
+ ///
+ /// # Implementation-specific behavior
+ ///
+ /// Uses the `sysdir(3)` API from `libSystem` to discover the standard
+ /// user directories.
+ ///
+ /// This behavior may change in the future. One example change that we
+ /// explicitly reserve the right to make is to load additional common
+ /// directories not currently in this list.
+ ///
+ /// [`cache_home`]: HomeDirs::cache_home
+ /// [`config_home`]: HomeDirs::config_home
+ /// [`data_home`]: HomeDirs::data_home
+ /// [`state_home`]: HomeDirs::state_home
+ ///
+ /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc
+ /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc
+ /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc
+ #[unstable(feature = "fs_home_dirs", issue = "162082")]
+ fn sysdir() -> io::Result;
+}
+
+/// Darwin-specific extensions to [`fs::MediaDirs`](MediaDirs).
+#[unstable(feature = "fs_media_dirs", issue = "162083")]
+pub impl(self) trait MediaDirsExt: Sized {
+ /// Load the standard user directory paths for the current user.
+ ///
+ /// The produced directory paths are not guaranteed to be the canonical
+ /// paths to the directories; they are allowed to be sandbox-redirected
+ /// paths as long as the directory is accessible there.
+ ///
+ /// The loaded common directories are:
+ ///
+ /// | `MediaDirs` | [`NSSearchPathDirectory`] |
+ /// | ---------- | ----------------------- |
+ /// | [`desktop`] | [`NSDesktopDirectory`] (`~/Desktop`) |
+ /// | [`documents`] | [`NSDocumentDirectory`] (`~/Documents`) |
+ /// | [`downloads`] | [`NSDownloadsDirectory`] |
+ /// | [`music`] | [`NSMusicDirectory`] (`~/Music`) |
+ /// | [`pictures`] | [`NSPicturesDirectory`] (`~/Pictures`) |
+ /// | [`videos`] | [`NSMoviesDirectory`] (`~/Movies`) |
+ ///
+ /// # Errors
+ ///
+ /// Errors if the the [user home](env::home_dir) cannot be determined.
+ // Errors due to the underlying sysdir(3) API should never occur, as
+ // - the user domain only has one directory for each search path;
+ // - the user domain always returns subdirectory paths of `~`;
+ // - the username plus OS defined path segments cannot exceed PATH_MAX; and
+ // - the username and OS defined path segments are always valid UTF-8.
+ ///
+ /// # Implementation-specific behavior
+ ///
+ /// Uses the `sysdir(3)` API from `libSystem` to discover the standard
+ /// user directories.
+ ///
+ /// This behavior may change in the future. One example change that we
+ /// explicitly reserve the right to make is to load additional common
+ /// directories not currently in this list.
+ ///
+ /// [`desktop`]: MediaDirs::desktop
+ /// [`documents`]: MediaDirs::documents
+ /// [`downloads`]: MediaDirs::downloads
+ /// [`music`]: MediaDirs::music
+ /// [`pictures`]: MediaDirs::pictures
+ /// [`videos`]: MediaDirs::videos
+ ///
+ /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc
+ /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc
+ /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc
+ /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc
+ /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc
+ /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc
+ /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc
+ #[unstable(feature = "fs_media_dirs", issue = "162083")]
+ fn sysdir() -> io::Result;
+}
+
+fn user_home() -> io::Result {
+ env::home_dir()
+ .filter(|p| p.is_absolute())
+ .ok_or(const_error!(ErrorKind::InvalidData, "home path not absolute"))
+}
+
+#[unstable(feature = "fs_home_dirs", issue = "162082")]
+#[cfg(target_vendor = "apple")]
+impl HomeDirsExt for HomeDirs {
+ fn sysdir() -> io::Result {
+ use libc::sysdir_search_path_directory_t::*;
+
+ let mut dirs = HomeDirs::empty();
+ let home = user_home()?;
+
+ let caches = sys::get_user_dir(&home, SYSDIR_DIRECTORY_CACHES)?;
+ let application_support = sys::get_user_dir(&home, SYSDIR_DIRECTORY_APPLICATION_SUPPORT)?;
+
+ dirs.cache = caches;
+ // Apple puts config/data/state all in Application Support
+ dirs.config = application_support.clone();
+ dirs.data = application_support.clone();
+ dirs.state = application_support;
+
+ Ok(dirs)
+ }
+}
+
+#[unstable(feature = "fs_media_dirs", issue = "162083")]
+#[cfg(target_vendor = "apple")]
+impl MediaDirsExt for MediaDirs {
+ fn sysdir() -> io::Result {
+ use libc::sysdir_search_path_directory_t::*;
+
+ let mut dirs = MediaDirs::empty();
+ let home = user_home()?;
+
+ let desktop = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DESKTOP)?;
+ let documents = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOCUMENT)?;
+ let downloads = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOWNLOADS)?;
+ let movies = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MOVIES)?;
+ let music = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MUSIC)?;
+ let pictures = sys::get_user_dir(&home, SYSDIR_DIRECTORY_PICTURES)?;
+
+ dirs.desktop = desktop;
+ dirs.documents = documents;
+ dirs.downloads = downloads;
+ dirs.music = music;
+ dirs.pictures = pictures;
+ dirs.videos = movies;
+
+ Ok(dirs)
+ }
+}
+
+/// Safer wrapper around the sysdir(3) API
+#[cfg(target_vendor = "apple")]
+mod sys {
+ use crate::ffi::{CStr, c_char};
+ use crate::io::{self, ErrorKind, const_error};
+ use crate::path::{Path, PathBuf};
+
+ /// Get the path for a system directory using `sysdir(3)`.
+ pub fn get_user_dir(
+ home: &Path,
+ kind: libc::sysdir_search_path_directory_t,
+ ) -> io::Result