diff --git a/crates/std/src/error.rs b/crates/std/src/error.rs new file mode 100644 index 0000000..d3e0090 --- /dev/null +++ b/crates/std/src/error.rs @@ -0,0 +1,4 @@ +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::error::Error; +#[unstable(feature = "error_generic_member_access", issue = "99301")] +pub use core::error::{Request, request_ref, request_value}; diff --git a/crates/std/src/fs.rs b/crates/std/src/fs.rs new file mode 100644 index 0000000..b9e3361 --- /dev/null +++ b/crates/std/src/fs.rs @@ -0,0 +1,3532 @@ +//! Filesystem manipulation operations. +//! +//! This module contains basic methods to manipulate the contents of the local +//! filesystem. All methods in this module represent cross-platform filesystem +//! operations. Extra platform-specific functionality can be found in the +//! extension traits of `std::os::$platform`. +//! +//! # Time of Check to Time of Use (TOCTOU) +//! +//! Many filesystem operations are subject to a race condition known as "Time of Check to Time of Use" +//! (TOCTOU). This occurs when a program checks a condition (like file existence or permissions) +//! and then uses the result of that check to make a decision, but the condition may have changed +//! between the check and the use. +//! +//! For example, checking if a file exists and then creating it if it doesn't is vulnerable to +//! TOCTOU - another process could create the file between your check and creation attempt. +//! +//! Another example is with symbolic links: when removing a directory, if another process replaces +//! the directory with a symbolic link between the check and the removal operation, the removal +//! might affect the wrong location. This is why operations like [`remove_dir_all`] need to use +//! atomic operations to prevent such race conditions. +//! +//! To avoid TOCTOU issues: +//! - Be aware that metadata operations (like [`metadata`] or [`symlink_metadata`]) may be affected by +//! changes made by other processes. +//! - Use atomic operations when possible (like [`File::create_new`] instead of checking existence then creating). +//! - Keep file open for the duration of operations. + +#![stable(feature = "rust1", since = "1.0.0")] +#![deny(unsafe_op_in_unsafe_fn)] + +#[cfg(all( + test, + not(any( + target_os = "emscripten", + target_os = "wasi", + target_env = "sgx", + target_os = "xous", + target_os = "survos", + target_os = "trusty", + )) +))] +mod tests; + +use crate::ffi::OsString; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::path::{Path, PathBuf}; +use crate::sealed::Sealed; +use alloc::sync::Arc; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp}; +use crate::time::SystemTime; +use crate::{error, fmt}; + +/// 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 +/// it was opened with. Files also implement [`Seek`] to alter the logical cursor +/// that the file contains internally. +/// +/// Files are automatically closed when they go out of scope. Errors detected +/// on closing are ignored by the implementation of `Drop`. Use the method +/// [`sync_all`] if these errors must be manually handled. +/// +/// `File` does not buffer reads and writes. For efficiency, consider wrapping the +/// file in a [`BufReader`] or [`BufWriter`] when performing many small [`read`] +/// or [`write`] calls, unless unbuffered reads and writes are required. +/// +/// # Examples +/// +/// Creates a new file and write bytes to it (you can also use [`write`]): +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut file = File::create("foo.txt")?; +/// file.write_all(b"Hello, world!")?; +/// Ok(()) +/// } +/// ``` +/// +/// Reads the contents of a file into a [`String`] (you can also use [`read`]): +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut file = File::open("foo.txt")?; +/// let mut contents = String::new(); +/// file.read_to_string(&mut contents)?; +/// assert_eq!(contents, "Hello, world!"); +/// Ok(()) +/// } +/// ``` +/// +/// Using a buffered [`Read`]er: +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::BufReader; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let file = File::open("foo.txt")?; +/// let mut buf_reader = BufReader::new(file); +/// let mut contents = String::new(); +/// buf_reader.read_to_string(&mut contents)?; +/// assert_eq!(contents, "Hello, world!"); +/// Ok(()) +/// } +/// ``` +/// +/// Note that, although read and write methods require a `&mut File`, because +/// of the interfaces for [`Read`] and [`Write`], the holder of a `&File` can +/// still modify the file, either through methods that take `&File` or by +/// retrieving the underlying OS object and modifying the file that way. +/// Additionally, many operating systems allow concurrent modification of files +/// by different processes. Avoid assuming that holding a `&File` means that the +/// file will not change. +/// +/// # Platform-specific behavior +/// +/// On Windows, the implementation of [`Read`] and [`Write`] traits for `File` +/// perform synchronous I/O operations. Therefore the underlying file must not +/// have been opened for asynchronous I/O (e.g. by using `FILE_FLAG_OVERLAPPED`). +/// +/// [`BufReader`]: io::BufReader +/// [`BufWriter`]: io::BufWriter +/// [`sync_all`]: File::sync_all +/// [`write`]: File::write +/// [`read`]: File::read +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "File")] +pub struct File { + inner: fs_imp::File, +} + +/// An enumeration of possible errors which can occur while trying to acquire a lock +/// from the [`try_lock`] method and [`try_lock_shared`] method on a [`File`]. +/// +/// [`try_lock`]: File::try_lock +/// [`try_lock_shared`]: File::try_lock_shared +#[stable(feature = "file_lock", since = "1.89.0")] +pub enum TryLockError { + /// The lock could not be acquired due to an I/O error on the file. The standard library will + /// not return an [`ErrorKind::WouldBlock`] error inside [`TryLockError::Error`] + /// + /// [`ErrorKind::WouldBlock`]: io::ErrorKind::WouldBlock + Error(io::Error), + /// The lock could not be acquired at this time because it is held by another handle/process. + WouldBlock, +} + +/// An object providing access to a directory on the filesystem. +/// +/// Directories are automatically closed when they go out of scope. Errors detected +/// on closing are ignored by the implementation of `Drop`. +/// +/// # Platform-specific behavior +/// +/// On supported systems (including Windows and some UNIX-based OSes), this function acquires a +/// handle/file descriptor for the directory. This allows functions like [`Dir::open_file`] to +/// avoid [TOCTOU] errors when the directory itself is being moved. +/// +/// On other systems, it stores an absolute path (see [`canonicalize()`]). In the latter case, no +/// [TOCTOU] guarantees are made. +/// +/// # Examples +/// +/// Opens a directory and then a file inside it. +/// +/// ```no_run +/// #![feature(dirfd)] +/// use std::{fs::Dir, io}; +/// +/// fn main() -> std::io::Result<()> { +/// let dir = Dir::open("foo")?; +/// let mut file = dir.open_file("bar.txt")?; +/// let contents = io::read_to_string(file)?; +/// assert_eq!(contents, "Hello, world!"); +/// Ok(()) +/// } +/// ``` +/// +/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou +#[unstable(feature = "dirfd", issue = "120426")] +pub struct Dir { + inner: fs_imp::Dir, +} + +/// Metadata information about a file. +/// +/// This structure is returned from the [`metadata`] or +/// [`symlink_metadata`] function or method and represents known +/// metadata about a file such as its permissions, size, modification +/// times, etc. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct Metadata(fs_imp::FileAttr); + +/// Iterator over the entries in a directory. +/// +/// This iterator is returned from the [`read_dir`] function of this module and +/// will yield instances of [io::Result]<[DirEntry]>. Through a [`DirEntry`] +/// information like the entry's path and possibly other metadata can be +/// learned. +/// +/// The order in which this iterator returns entries is platform and filesystem +/// dependent. +/// +/// # Errors +/// This [`io::Result`] will be an [`Err`] if an error occurred while fetching +/// the next entry from the OS. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct ReadDir(fs_imp::ReadDir); + +/// Entries returned by the [`ReadDir`] iterator. +/// +/// An instance of `DirEntry` represents an entry inside of a directory on the +/// filesystem. Each entry can be inspected via methods to learn about the full +/// path or possibly other metadata through per-platform extension traits. +/// +/// # Platform-specific behavior +/// +/// On Unix, the `DirEntry` struct contains an internal reference to the open +/// directory. Holding `DirEntry` objects will consume a file handle even +/// after the `ReadDir` iterator is dropped. +/// +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +#[stable(feature = "rust1", since = "1.0.0")] +pub struct DirEntry(fs_imp::DirEntry); + +/// Options and flags which can be used to configure how a file is opened. +/// +/// This builder exposes the ability to configure how a [`File`] is opened and +/// what operations are permitted on the open file. The [`File::open`] and +/// [`File::create`] methods are aliases for commonly used options using this +/// builder. +/// +/// Generally speaking, when using `OpenOptions`, you'll first call +/// [`OpenOptions::new`], then chain calls to methods to set each option, then +/// call [`OpenOptions::open`], passing the path of the file you're trying to +/// open. This will give you a [`io::Result`] with a [`File`] inside that you +/// can further operate on. +/// +/// # Examples +/// +/// Opening a file to read: +/// +/// ```no_run +/// use std::fs::OpenOptions; +/// +/// let file = OpenOptions::new().read(true).open("foo.txt"); +/// ``` +/// +/// Opening a file for both reading and writing, as well as creating it if it +/// doesn't exist: +/// +/// ```no_run +/// use std::fs::OpenOptions; +/// +/// let file = OpenOptions::new() +/// .read(true) +/// .write(true) +/// .create(true) +/// .open("foo.txt"); +/// ``` +#[derive(Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "FsOpenOptions")] +pub struct OpenOptions(fs_imp::OpenOptions); + +/// Representation of the various timestamps on a file. +#[derive(Copy, Clone, Debug, Default)] +#[stable(feature = "file_set_times", since = "1.75.0")] +#[must_use = "must be applied to a file via `File::set_times` to have any effect"] +pub struct FileTimes(fs_imp::FileTimes); + +/// Representation of the various permissions on a file. +/// +/// This module only currently provides one bit of information, +/// [`Permissions::readonly`], which is exposed on all currently supported +/// platforms. Unix-specific functionality, such as mode bits, is available +/// through the [`PermissionsExt`] trait. +/// +/// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt +#[derive(Clone, PartialEq, Eq, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "FsPermissions")] +pub struct Permissions(fs_imp::FilePermissions); + +/// A structure representing a type of file with accessors for each file type. +/// It is returned by [`Metadata::file_type`] method. +#[stable(feature = "file_type", since = "1.1.0")] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(not(test), rustc_diagnostic_item = "FileType")] +pub struct FileType(fs_imp::FileType); + +/// A builder used to create directories in various manners. +/// +/// This builder also supports platform-specific options. +#[stable(feature = "dir_builder", since = "1.6.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "DirBuilder")] +#[derive(Debug)] +pub struct DirBuilder { + inner: fs_imp::DirBuilder, + recursive: bool, +} + +/// Reads the entire contents of a file into a bytes vector. +/// +/// This is a convenience function for using [`File::open`] and [`read_to_end`] +/// with fewer imports and without an intermediate variable. +/// +/// [`read_to_end`]: Read::read_to_end +/// +/// # Errors +/// +/// This function will return an error if `path` does not already exist. +/// Other errors may also be returned according to [`OpenOptions::open`]. +/// +/// While reading from the file, this function handles [`io::ErrorKind::Interrupted`] +/// with automatic retries. See [io::Read] documentation for details. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> Result<(), Box> { +/// let data: Vec = fs::read("image.jpg")?; +/// assert_eq!(data[0..3], [0xFF, 0xD8, 0xFF]); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "fs_read_write_bytes", since = "1.26.0")] +pub fn read>(path: P) -> io::Result> { + fn inner(path: &Path) -> io::Result> { + let mut file = File::open(path)?; + let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok(); + let mut bytes = Vec::try_with_capacity(size.unwrap_or(0))?; + io::default_read_to_end(&mut file, &mut bytes, size)?; + Ok(bytes) + } + inner(path.as_ref()) +} + +/// Reads the entire contents of a file into a string. +/// +/// This is a convenience function for using [`File::open`] and [`read_to_string`] +/// with fewer imports and without an intermediate variable. +/// +/// [`read_to_string`]: Read::read_to_string +/// +/// # Errors +/// +/// This function will return an error if `path` does not already exist. +/// Other errors may also be returned according to [`OpenOptions::open`]. +/// +/// If the contents of the file are not valid UTF-8, then an error will also be +/// returned. +/// +/// While reading from the file, this function handles [`io::ErrorKind::Interrupted`] +/// with automatic retries. See [io::Read] documentation for details. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// use std::error::Error; +/// +/// fn main() -> Result<(), Box> { +/// let message: String = fs::read_to_string("message.txt")?; +/// println!("{}", message); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "fs_read_write", since = "1.26.0")] +pub fn read_to_string>(path: P) -> io::Result { + fn inner(path: &Path) -> io::Result { + let mut file = File::open(path)?; + let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok(); + let mut string = String::new(); + string.try_reserve_exact(size.unwrap_or(0))?; + io::default_read_to_string(&mut file, &mut string, size)?; + Ok(string) + } + inner(path.as_ref()) +} + +/// Writes a slice as the entire contents of a file. +/// +/// This function will create a file if it does not exist, +/// and will entirely replace its contents if it does. +/// +/// Depending on the platform, this function may fail if the +/// full directory path does not exist. +/// +/// This is a convenience function for using [`File::create`] and [`write_all`] +/// with fewer imports. +/// +/// [`write_all`]: Write::write_all +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::write("foo.txt", b"Lorem ipsum")?; +/// fs::write("bar.txt", "dolor sit")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "fs_read_write_bytes", since = "1.26.0")] +pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + fn inner(path: &Path, contents: &[u8]) -> io::Result<()> { + File::create(path)?.write_all(contents) + } + inner(path.as_ref(), contents.as_ref()) +} + +/// Changes the timestamps of the file or directory at the specified path. +/// +/// This function will attempt to set the access and modification times +/// to the times specified. If the path refers to a symbolic link, this function +/// will follow the link and change the timestamps of the target file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `utimensat` function on Unix platforms, the +/// `setattrlist` function on Apple platforms, and the `SetFileTime` function on Windows. +/// +/// # Errors +/// +/// This function will return an error if the user lacks permission to change timestamps on the +/// target file or symlink. It may also return an error if the OS does not support it. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_set_times)] +/// use std::fs::{self, FileTimes}; +/// use std::time::SystemTime; +/// +/// fn main() -> std::io::Result<()> { +/// let now = SystemTime::now(); +/// let times = FileTimes::new() +/// .set_accessed(now) +/// .set_modified(now); +/// fs::set_times("foo.txt", times)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "fs_set_times", issue = "147455")] +#[doc(alias = "utimens")] +#[doc(alias = "utimes")] +#[doc(alias = "utime")] +pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { + fs_imp::set_times(path.as_ref(), times.0) +} + +/// Changes the timestamps of the file or symlink at the specified path. +/// +/// This function will attempt to set the access and modification times +/// to the times specified. Differ from `set_times`, if the path refers to a symbolic link, +/// this function will change the timestamps of the symlink itself, not the target file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW` on +/// Unix platforms, the `setattrlist` function with `FSOPT_NOFOLLOW` on Apple platforms, and the +/// `SetFileTime` function on Windows. +/// +/// # Errors +/// +/// This function will return an error if the user lacks permission to change timestamps on the +/// target file or symlink. It may also return an error if the OS does not support it. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_set_times)] +/// use std::fs::{self, FileTimes}; +/// use std::time::SystemTime; +/// +/// fn main() -> std::io::Result<()> { +/// let now = SystemTime::now(); +/// let times = FileTimes::new() +/// .set_accessed(now) +/// .set_modified(now); +/// fs::set_times_nofollow("symlink.txt", times)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "fs_set_times", issue = "147455")] +#[doc(alias = "utimensat")] +#[doc(alias = "lutimens")] +#[doc(alias = "lutimes")] +pub fn set_times_nofollow>(path: P, times: FileTimes) -> io::Result<()> { + fs_imp::set_times_nofollow(path.as_ref(), times.0) +} + +#[stable(feature = "file_lock", since = "1.89.0")] +impl error::Error for TryLockError {} + +#[stable(feature = "file_lock", since = "1.89.0")] +impl fmt::Debug for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TryLockError::Error(err) => err.fmt(f), + TryLockError::WouldBlock => "WouldBlock".fmt(f), + } + } +} + +#[stable(feature = "file_lock", since = "1.89.0")] +impl fmt::Display for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TryLockError::Error(_) => "lock acquisition failed due to I/O error", + TryLockError::WouldBlock => "lock acquisition failed because the operation would block", + } + .fmt(f) + } +} + +#[stable(feature = "file_lock", since = "1.89.0")] +impl From for io::Error { + fn from(err: TryLockError) -> io::Error { + match err { + TryLockError::Error(err) => err, + TryLockError::WouldBlock => io::ErrorKind::WouldBlock.into(), + } + } +} + +impl File { + /// Attempts to open a file in read-only mode. + /// + /// See the [`OpenOptions::open`] method for more details. + /// + /// If you only need to read the entire file contents, + /// consider [`std::fs::read()`][self::read] or + /// [`std::fs::read_to_string()`][self::read_to_string] instead. + /// + /// # Errors + /// + /// This function will return an error if `path` does not already exist. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::Read; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut data = vec![]; + /// f.read_to_end(&mut data)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn open>(path: P) -> io::Result { + OpenOptions::new().read(true).open(path.as_ref()) + } + + /// Attempts to open a file in read-only mode with buffering. + /// + /// See the [`OpenOptions::open`] method, the [`BufReader`][io::BufReader] type, + /// and the [`BufRead`][io::BufRead] trait for more details. + /// + /// If you only need to read the entire file contents, + /// consider [`std::fs::read()`][self::read] or + /// [`std::fs::read_to_string()`][self::read_to_string] instead. + /// + /// # Errors + /// + /// This function will return an error if `path` does not already exist, + /// or if memory allocation fails for the new buffer. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_buffered)] + /// use std::fs::File; + /// use std::io::BufRead; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::open_buffered("foo.txt")?; + /// assert!(f.capacity() > 0); + /// for (line, i) in f.lines().zip(1..) { + /// println!("{i:6}: {}", line?); + /// } + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_buffered", issue = "130804")] + pub fn open_buffered>(path: P) -> io::Result> { + // Allocate the buffer *first* so we don't affect the filesystem otherwise. + let buffer = io::BufReader::::try_new_buffer()?; + let file = File::open(path)?; + Ok(io::BufReader::with_buffer(file, buffer)) + } + + /// Opens a file in write-only mode. + /// + /// This function will create a file if it does not exist, + /// and will truncate it if it does. + /// + /// Depending on the platform, this function may fail if the + /// full directory path does not exist. + /// See the [`OpenOptions::open`] function for more details. + /// + /// See also [`std::fs::write()`][self::write] for a simple function to + /// create a file with some given data. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::Write; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(&1234_u32.to_be_bytes())?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn create>(path: P) -> io::Result { + OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()) + } + + /// Opens a file in write-only mode with buffering. + /// + /// This function will create a file if it does not exist, + /// and will truncate it if it does. + /// + /// Depending on the platform, this function may fail if the + /// full directory path does not exist. + /// + /// See the [`OpenOptions::open`] method and the + /// [`BufWriter`][io::BufWriter] type for more details. + /// + /// See also [`std::fs::write()`][self::write] for a simple function to + /// create a file with some given data. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_buffered)] + /// use std::fs::File; + /// use std::io::Write; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create_buffered("foo.txt")?; + /// assert!(f.capacity() > 0); + /// for i in 0..100 { + /// writeln!(&mut f, "{i}")?; + /// } + /// f.flush()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_buffered", issue = "130804")] + pub fn create_buffered>(path: P) -> io::Result> { + // Allocate the buffer *first* so we don't affect the filesystem otherwise. + let buffer = io::BufWriter::::try_new_buffer()?; + let file = File::create(path)?; + Ok(io::BufWriter::with_buffer(file, buffer)) + } + + /// Creates a new file in read-write mode; error if the file exists. + /// + /// This function will create a file if it does not exist, or return an error if it does. This + /// way, if the call succeeds, the file returned is guaranteed to be new. + /// If a file exists at the target location, creating a new file will fail with [`AlreadyExists`] + /// or another error based on the situation. See [`OpenOptions::open`] for a + /// non-exhaustive list of likely errors. + /// + /// This option is useful because it is atomic. Otherwise between checking whether a file + /// exists and creating a new one, the file may have been created by another process (a [TOCTOU] + /// race condition / attack). + /// + /// This can also be written using + /// `File::options().read(true).write(true).create_new(true).open(...)`. + /// + /// [`AlreadyExists`]: crate::io::ErrorKind::AlreadyExists + /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::Write; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create_new("foo.txt")?; + /// f.write_all("Hello, world!".as_bytes())?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_create_new", since = "1.77.0")] + pub fn create_new>(path: P) -> io::Result { + OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref()) + } + + /// Returns a new OpenOptions object. + /// + /// This function returns a new OpenOptions object that you can use to + /// open or create a file with specific options if `open()` or `create()` + /// are not appropriate. + /// + /// It is equivalent to `OpenOptions::new()`, but allows you to write more + /// readable code. Instead of + /// `OpenOptions::new().append(true).open("example.log")`, + /// you can write `File::options().append(true).open("example.log")`. This + /// also avoids the need to import `OpenOptions`. + /// + /// See the [`OpenOptions::new`] function for more details. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::Write; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::options().append(true).open("example.log")?; + /// writeln!(&mut f, "new line")?; + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "with_options", since = "1.58.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "file_options")] + pub fn options() -> OpenOptions { + OpenOptions::new() + } + + /// Attempts to sync all OS-internal file content and metadata to disk. + /// + /// This function will attempt to ensure that all in-memory data reaches the + /// filesystem before returning. + /// + /// This can be used to handle errors that would otherwise only be caught + /// when the `File` is closed, as dropping a `File` will ignore all errors. + /// Note, however, that `sync_all` is generally more expensive than closing + /// a file by dropping it, because the latter is not required to block until + /// the data has been written to the filesystem. + /// + /// If synchronizing the metadata is not required, use [`sync_data`] instead. + /// + /// [`sync_data`]: File::sync_data + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello, world!")?; + /// + /// f.sync_all()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(alias = "fsync")] + pub fn sync_all(&self) -> io::Result<()> { + self.inner.fsync() + } + + /// This function is similar to [`sync_all`], except that it might not + /// synchronize file metadata to the filesystem. + /// + /// This is intended for use cases that must synchronize content, but don't + /// need the metadata on disk. The goal of this method is to reduce disk + /// operations. + /// + /// Note that some platforms may simply implement this in terms of + /// [`sync_all`]. + /// + /// [`sync_all`]: File::sync_all + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello, world!")?; + /// + /// f.sync_data()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(alias = "fdatasync")] + pub fn sync_data(&self) -> io::Result<()> { + self.inner.datasync() + } + + /// Acquire an exclusive lock on the file. Blocks until the lock can be acquired. + /// + /// This acquires an exclusive lock; no other file handle to this file may acquire another lock. + /// + /// This lock may be advisory or mandatory. This lock is meant to interact with [`lock`], + /// [`try_lock`], [`lock_shared`], [`try_lock_shared`], and [`unlock`]. Its interactions with + /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not + /// cause non-lockholders to block. + /// + /// If this file handle/descriptor, or a clone of it, already holds a lock the exact behavior + /// is unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then an exclusive lock is held. + /// + /// If the file is not open for writing, it is unspecified whether this function returns an error. + /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, + /// and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK` flag. Note that, + /// this [may change in the future][changes]. + /// + /// On Windows, locking a file will fail if the file is opened only for append. To lock a file, + /// open it with one of `.read(true)`, `.read(true).append(true)`, or `.write(true)`. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// f.lock()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_lock", since = "1.89.0")] + pub fn lock(&self) -> io::Result<()> { + self.inner.lock() + } + + /// Acquire a shared (non-exclusive) lock on the file. Blocks until the lock can be acquired. + /// + /// This acquires a shared lock; more than one file handle may hold a shared lock, but none may + /// hold an exclusive lock at the same time. + /// + /// This lock may be advisory or mandatory. This lock is meant to interact with [`lock`], + /// [`try_lock`], [`lock_shared`], [`try_lock_shared`], and [`unlock`]. Its interactions with + /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not + /// cause non-lockholders to block. + /// + /// If this file handle/descriptor, or a clone of it, already holds a lock, the exact behavior + /// is unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then a shared lock is held. + /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, + /// and the `LockFileEx` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// On Windows, locking a file will fail if the file is opened only for append. To lock a file, + /// open it with one of `.read(true)`, `.read(true).append(true)`, or `.write(true)`. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock_shared()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_lock", since = "1.89.0")] + pub fn lock_shared(&self) -> io::Result<()> { + self.inner.lock_shared() + } + + /// Try to acquire an exclusive lock on the file. + /// + /// Returns `Err(TryLockError::WouldBlock)` if a different lock is already held on this file + /// (via another handle/descriptor). + /// + /// This acquires an exclusive lock; no other file handle to this file may acquire another lock. + /// + /// This lock may be advisory or mandatory. This lock is meant to interact with [`lock`], + /// [`try_lock`], [`lock_shared`], [`try_lock_shared`], and [`unlock`]. Its interactions with + /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not + /// cause non-lockholders to block. + /// + /// If this file handle/descriptor, or a clone of it, already holds a lock, the exact behavior + /// is unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns `Ok(())`, then it has acquired an exclusive lock. + /// + /// If the file is not open for writing, it is unspecified whether this function returns an error. + /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and + /// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK` + /// and `LOCKFILE_FAIL_IMMEDIATELY` flags. Note that, this + /// [may change in the future][changes]. + /// + /// On Windows, locking a file will fail if the file is opened only for append. To lock a file, + /// open it with one of `.read(true)`, `.read(true).append(true)`, or `.write(true)`. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::{File, TryLockError}; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// // Explicit handling of the WouldBlock error + /// match f.try_lock() { + /// Ok(_) => (), + /// Err(TryLockError::WouldBlock) => (), // Lock not acquired + /// Err(TryLockError::Error(err)) => return Err(err), + /// } + /// // Alternately, propagate the error as an io::Error + /// f.try_lock()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_lock", since = "1.89.0")] + pub fn try_lock(&self) -> Result<(), TryLockError> { + self.inner.try_lock() + } + + /// Try to acquire a shared (non-exclusive) lock on the file. + /// + /// Returns `Err(TryLockError::WouldBlock)` if a different lock is already held on this file + /// (via another handle/descriptor). + /// + /// This acquires a shared lock; more than one file handle may hold a shared lock, but none may + /// hold an exclusive lock at the same time. + /// + /// This lock may be advisory or mandatory. This lock is meant to interact with [`lock`], + /// [`try_lock`], [`lock_shared`], [`try_lock_shared`], and [`unlock`]. Its interactions with + /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not + /// cause non-lockholders to block. + /// + /// If this file handle, or a clone of it, already holds a lock, the exact behavior is + /// unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns `Ok(())`, then it has acquired a shared lock. + /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and + /// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the + /// `LOCKFILE_FAIL_IMMEDIATELY` flag. Note that, this + /// [may change in the future][changes]. + /// + /// On Windows, locking a file will fail if the file is opened only for append. To lock a file, + /// open it with one of `.read(true)`, `.read(true).append(true)`, or `.write(true)`. + /// + /// [changes]: io#platform-specific-behavior + /// + /// [`lock`]: File::lock + /// [`lock_shared`]: File::lock_shared + /// [`try_lock`]: File::try_lock + /// [`try_lock_shared`]: File::try_lock_shared + /// [`unlock`]: File::unlock + /// [`read`]: Read::read + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::{File, TryLockError}; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// // Explicit handling of the WouldBlock error + /// match f.try_lock_shared() { + /// Ok(_) => (), + /// Err(TryLockError::WouldBlock) => (), // Lock not acquired + /// Err(TryLockError::Error(err)) => return Err(err), + /// } + /// // Alternately, propagate the error as an io::Error + /// f.try_lock_shared()?; + /// + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_lock", since = "1.89.0")] + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + self.inner.try_lock_shared() + } + + /// Release all locks on the file. + /// + /// All locks are released when the file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed. This method allows releasing locks without + /// closing the file. + /// + /// If no lock is currently held via this file descriptor/handle, this method may return an + /// error, or may return successfully without taking any action. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `flock` function on Unix with the `LOCK_UN` flag, + /// and the `UnlockFile` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// On Windows, locking a file will fail if the file is opened only for append. To lock a file, + /// open it with one of `.read(true)`, `.read(true).append(true)`, or `.write(true)`. + /// + /// [changes]: io#platform-specific-behavior + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("foo.txt")?; + /// f.lock()?; + /// f.unlock()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_lock", since = "1.89.0")] + pub fn unlock(&self) -> io::Result<()> { + self.inner.unlock() + } + + /// Truncates or extends the underlying file, updating the size of + /// this file to become `size`. + /// + /// If the `size` is less than the current file's size, then the file will + /// be shrunk. If it is greater than the current file's size, then the file + /// will be extended to `size` and have all of the intermediate data filled + /// in with 0s. + /// + /// The file's cursor isn't changed. In particular, if the cursor was at the + /// end and the file is shrunk using this operation, the cursor will now be + /// past the end. + /// + /// # Errors + /// + /// This function will return an error if the file is not opened for writing. + /// Also, [`std::io::ErrorKind::InvalidInput`](crate::io::ErrorKind::InvalidInput) + /// will be returned if the desired length would cause an overflow due to + /// the implementation specifics. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.set_len(10)?; + /// Ok(()) + /// } + /// ``` + /// + /// Note that this method alters the content of the underlying file, even + /// though it takes `&self` rather than `&mut self`. + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_len(&self, size: u64) -> io::Result<()> { + self.inner.truncate(size) + } + + /// Queries metadata about the underlying file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let metadata = f.metadata()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn metadata(&self) -> io::Result { + self.inner.file_attr().map(Metadata) + } + + /// Creates a new `File` instance that shares the same underlying file handle + /// as the existing `File` instance. Reads, writes, and seeks will affect + /// both `File` instances simultaneously. + /// + /// # Examples + /// + /// Creates two handles for a file named `foo.txt`: + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let file_copy = file.try_clone()?; + /// Ok(()) + /// } + /// ``` + /// + /// Assuming there’s a file named `foo.txt` with contents `abcdef\n`, create + /// two handles, seek one of them, and read the remaining bytes from the + /// other handle: + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::SeekFrom; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let mut file_copy = file.try_clone()?; + /// + /// file.seek(SeekFrom::Start(3))?; + /// + /// let mut contents = vec![]; + /// file_copy.read_to_end(&mut contents)?; + /// assert_eq!(contents, b"def\n"); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_try_clone", since = "1.9.0")] + pub fn try_clone(&self) -> io::Result { + Ok(File { inner: self.inner.duplicate()? }) + } + + /// Changes the permissions on the underlying file. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `fchmod` function on Unix and + /// the `SetFileInformationByHandle` function on Windows. Note that, this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// # Errors + /// + /// This function will return an error if the user lacks permission change + /// attributes on the underlying file. It may also return an error in other + /// os-specific unspecified cases. + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs::File; + /// + /// let file = File::open("foo.txt")?; + /// let mut perms = file.metadata()?.permissions(); + /// perms.set_readonly(true); + /// file.set_permissions(perms)?; + /// Ok(()) + /// } + /// ``` + /// + /// Note that this method alters the permissions of the underlying file, + /// even though it takes `&self` rather than `&mut self`. + #[doc(alias = "fchmod", alias = "SetFileInformationByHandle")] + #[stable(feature = "set_permissions_atomic", since = "1.16.0")] + pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> { + self.inner.set_permissions(perm.0) + } + + /// Changes the timestamps of the underlying file. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `futimens` function on Unix (falling back to + /// `futimes` on macOS before 10.13) and the `SetFileTime` function on Windows. Note that this + /// [may change in the future][changes]. + /// + /// On most platforms, including UNIX and Windows platforms, this function can also change the + /// timestamps of a directory. To get a `File` representing a directory in order to call + /// `set_times`, open the directory with `File::open` without attempting to obtain write + /// permission. + /// + /// [changes]: io#platform-specific-behavior + /// + /// # Errors + /// + /// This function will return an error if the user lacks permission to change timestamps on the + /// underlying file. It may also return an error in other os-specific unspecified cases. + /// + /// This function may return an error if the operating system lacks support to change one or + /// more of the timestamps set in the `FileTimes` structure. + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs::{self, File, FileTimes}; + /// + /// let src = fs::metadata("src")?; + /// let dest = File::open("dest")?; + /// let times = FileTimes::new() + /// .set_accessed(src.accessed()?) + /// .set_modified(src.modified()?); + /// dest.set_times(times)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_set_times", since = "1.75.0")] + #[doc(alias = "futimens")] + #[doc(alias = "futimes")] + #[doc(alias = "SetFileTime")] + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + self.inner.set_times(times.0) + } + + /// Changes the modification time of the underlying file. + /// + /// This is an alias for `set_times(FileTimes::new().set_modified(time))`. + #[stable(feature = "file_set_times", since = "1.75.0")] + #[inline] + pub fn set_modified(&self, time: SystemTime) -> io::Result<()> { + self.set_times(FileTimes::new().set_modified(time)) + } +} + +// In addition to the `impl`s here, `File` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &fs_imp::File { + &self.inner + } +} +impl FromInner for File { + fn from_inner(f: fs_imp::File) -> File { + File { inner: f } + } +} +impl IntoInner for File { + fn into_inner(self) -> fs_imp::File { + self.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +/// Indicates how much extra capacity is needed to read the rest of the file. +fn buffer_capacity_required(mut file: &File) -> Option { + let size = file.metadata().map(|m| m.len()).ok()?; + let pos = file.stream_position().ok()?; + // Don't worry about `usize` overflow because reading will fail regardless + // in that case. + Some(size.saturating_sub(pos) as usize) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for &File { + /// Reads some bytes from the file. + /// + /// See [`Read::read`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `read` function on Unix and + /// the `NtReadFile` function on Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + /// Like `read`, except that it reads into a slice of buffers. + /// + /// See [`Read::read_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `readv` function on Unix and + /// falls back to the `read` implementation on Windows. Note that this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + #[inline] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf(cursor) + } + + /// Determines if `File` has an efficient `read_vectored` implementation. + /// + /// See [`Read::is_read_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently returns `true` on Unix and `false` on Windows. + /// Note that this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + // Reserves space in the buffer based on the file size when available. + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + let size = buffer_capacity_required(self); + buf.try_reserve(size.unwrap_or(0))?; + io::default_read_to_end(self, buf, size) + } + + // Reserves space in the buffer based on the file size when available. + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + let size = buffer_capacity_required(self); + buf.try_reserve(size.unwrap_or(0))?; + io::default_read_to_string(self, buf, size) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &File { + /// Writes some bytes to the file. + /// + /// See [`Write::write`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `write` function on Unix and + /// the `NtWriteFile` function on Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + + /// Like `write`, except that it writes into a slice of buffers. + /// + /// See [`Write::write_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `writev` function on Unix + /// and falls back to the `write` implementation on Windows. Note that this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + /// Determines if `File` has an efficient `write_vectored` implementation. + /// + /// See [`Write::is_write_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently returns `true` on Unix and `false` on Windows. + /// Note that this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + /// Flushes the file, ensuring that all intermediately buffered contents + /// reach their destination. + /// + /// See [`Write::flush`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// Since a `File` structure doesn't contain any buffers, this function is + /// currently a no-op on Unix and Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + #[inline] + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for &File { + /// Seek to an offset, in bytes in a file. + /// + /// See [`Seek::seek`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `lseek64` function on Unix + /// and the `SetFilePointerEx` function on Windows. Note that this [may + /// change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.inner.seek(pos) + } + + /// Returns the length of this file (in bytes). + /// + /// See [`Seek::stream_len`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `statx` function on Linux + /// (with fallbacks) and the `GetFileSizeEx` function on Windows. Note that + /// this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + fn stream_len(&mut self) -> io::Result { + if let Some(result) = self.inner.size() { + return result; + } + io::stream_len_default(self) + } + + fn stream_position(&mut self) -> io::Result { + self.inner.tell() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&*self).read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (&*self).read_vectored(bufs) + } + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (&*self).read_buf(cursor) + } + #[inline] + fn is_read_vectored(&self) -> bool { + (&&*self).is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (&*self).read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (&*self).read_to_string(buf) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for File { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&*self).write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&*self).write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + (&&*self).is_write_vectored() + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&*self).flush() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for File { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (&*self).seek(pos) + } + fn stream_len(&mut self) -> io::Result { + (&*self).stream_len() + } + fn stream_position(&mut self) -> io::Result { + (&*self).stream_position() + } +} + +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Read for Arc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&**self).read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (&**self).read_vectored(bufs) + } + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (&**self).read_buf(cursor) + } + #[inline] + fn is_read_vectored(&self) -> bool { + (&**self).is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (&**self).read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (&**self).read_to_string(buf) + } +} +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Write for Arc { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&**self).write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&**self).write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&**self).flush() + } +} +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Seek for Arc { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (&**self).seek(pos) + } + fn stream_len(&mut self) -> io::Result { + (&**self).stream_len() + } + fn stream_position(&mut self) -> io::Result { + (&**self).stream_position() + } +} + +impl Dir { + /// Attempts to open a directory at `path` in read-only mode. + /// + /// # Errors + /// + /// This function will return an error if `path` does not point to an existing directory. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::{fs::Dir, io}; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let mut f = dir.open_file("bar.txt")?; + /// let contents = io::read_to_string(f)?; + /// assert_eq!(contents, "Hello, world!"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn open>(path: P) -> io::Result { + fs_imp::Dir::open(path.as_ref(), &OpenOptions::new().read(true).0) + .map(|inner| Self { inner }) + } + + /// Attempts to open a file in read-only mode relative to this directory. + /// + /// # Errors + /// + /// This function will return an error if `path` does not point to an existing file. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::{fs::Dir, io}; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let mut f = dir.open_file("bar.txt")?; + /// let contents = io::read_to_string(f)?; + /// assert_eq!(contents, "Hello, world!"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn open_file>(&self, path: P) -> io::Result { + self.inner + .open_file(path.as_ref(), &OpenOptions::new().read(true).0) + .map(|f| File { inner: f }) + } +} + +impl AsInner for Dir { + #[inline] + fn as_inner(&self) -> &fs_imp::Dir { + &self.inner + } +} +impl FromInner for Dir { + fn from_inner(f: fs_imp::Dir) -> Dir { + Dir { inner: f } + } +} +impl IntoInner for Dir { + fn into_inner(self) -> fs_imp::Dir { + self.inner + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl OpenOptions { + /// Creates a blank new set of options ready for configuration. + /// + /// All options are initially set to `false`. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let mut options = OpenOptions::new(); + /// let file = options.read(true).open("foo.txt"); + /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "open_options_new")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn new() -> Self { + OpenOptions(fs_imp::OpenOptions::new()) + } + + /// Sets the option for read access. + /// + /// This option, when true, will indicate that the file should be + /// `read`-able if opened. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().read(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn read(&mut self, read: bool) -> &mut Self { + self.0.read(read); + self + } + + /// Sets the option for write access. + /// + /// This option, when true, will indicate that the file should be + /// `write`-able if opened. + /// + /// If the file already exists, any write calls on it will overwrite its + /// contents, without truncating it. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn write(&mut self, write: bool) -> &mut Self { + self.0.write(write); + self + } + + /// Sets the option for the append mode. + /// + /// This option, when true, means that writes will append to a file instead + /// of overwriting previous contents. + /// Note that setting `.write(true).append(true)` has the same effect as + /// setting only `.append(true)`. + /// + /// Append mode guarantees that writes will be positioned at the current end of file, + /// even when there are other processes or threads appending to the same file. This is + /// unlike [seek]\([SeekFrom]::[End]\(0)) followed by `write()`, which + /// has a race between seeking and writing during which another writer can write, with + /// our `write()` overwriting their data. + /// + /// Keep in mind that this does not necessarily guarantee that data appended by + /// different processes or threads does not interleave. The amount of data accepted a + /// single `write()` call depends on the operating system and file system. A + /// successful `write()` is allowed to write only part of the given data, so even if + /// you're careful to provide the whole message in a single call to `write()`, there + /// is no guarantee that it will be written out in full. If you rely on the filesystem + /// accepting the message in a single write, make sure that all data that belongs + /// together is written in one operation. This can be done by concatenating strings + /// before passing them to [`write()`]. + /// + /// If a file is opened with both read and append access, beware that after + /// opening, and after every write, the position for reading may be set at the + /// end of the file. So, before writing, save the current position (using + /// [Seek]::[stream_position]), and restore it before the next read. + /// + /// ## Note + /// + /// This function doesn't create the file if it doesn't exist. Use the + /// [`OpenOptions::create`] method to do so. + /// + /// [`write()`]: Write::write "io::Write::write" + /// [`flush()`]: Write::flush "io::Write::flush" + /// [stream_position]: Seek::stream_position "io::Seek::stream_position" + /// [seek]: Seek::seek "io::Seek::seek" + /// [Current]: SeekFrom::Current "io::SeekFrom::Current" + /// [End]: SeekFrom::End "io::SeekFrom::End" + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().append(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn append(&mut self, append: bool) -> &mut Self { + self.0.append(append); + self + } + + /// Sets the option for truncating a previous file. + /// + /// If a file is successfully opened with this option set to true, it will truncate + /// the file to 0 length if it already exists. + /// + /// The file must be opened with write access for truncate to work. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true).truncate(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn truncate(&mut self, truncate: bool) -> &mut Self { + self.0.truncate(truncate); + self + } + + /// Sets the option to create a new file, or open it if it already exists. + /// + /// In order for the file to be created, [`OpenOptions::write`] or + /// [`OpenOptions::append`] access must be used. + /// + /// See also [`std::fs::write()`][self::write] for a simple function to + /// create a file with some given data. + /// + /// # Errors + /// + /// If `.create(true)` is set without `.write(true)` or `.append(true)`, + /// calling [`open`](Self::open) will fail with [`InvalidInput`](io::ErrorKind::InvalidInput) error. + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true).create(true).open("foo.txt"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn create(&mut self, create: bool) -> &mut Self { + self.0.create(create); + self + } + + /// Sets the option to create a new file, failing if it already exists. + /// + /// No file is allowed to exist at the target location, also no (dangling) symlink. In this + /// way, if the call succeeds, the file returned is guaranteed to be new. + /// If a file exists at the target location, creating a new file will fail with [`AlreadyExists`] + /// or another error based on the situation. See [`OpenOptions::open`] for a + /// non-exhaustive list of likely errors. + /// + /// This option is useful because it is atomic. Otherwise between checking + /// whether a file exists and creating a new one, the file may have been + /// created by another process (a [TOCTOU] race condition / attack). + /// + /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are + /// ignored. + /// + /// The file must be opened with write or append access in order to create + /// a new file. + /// + /// [`.create()`]: OpenOptions::create + /// [`.truncate()`]: OpenOptions::truncate + /// [`AlreadyExists`]: io::ErrorKind::AlreadyExists + /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().write(true) + /// .create_new(true) + /// .open("foo.txt"); + /// ``` + #[stable(feature = "expand_open_options2", since = "1.9.0")] + pub fn create_new(&mut self, create_new: bool) -> &mut Self { + self.0.create_new(create_new); + self + } + + /// Opens a file at `path` with the options specified by `self`. + /// + /// # Errors + /// + /// This function will return an error under a number of different + /// circumstances. Some of these error conditions are listed here, together + /// with their [`io::ErrorKind`]. The mapping to [`io::ErrorKind`]s is not + /// part of the compatibility contract of the function. + /// + /// * [`NotFound`]: The specified file does not exist and neither `create` + /// or `create_new` is set. + /// * [`NotFound`]: One of the directory components of the file path does + /// not exist. + /// * [`PermissionDenied`]: The user lacks permission to get the specified + /// access rights for the file. + /// * [`PermissionDenied`]: The user lacks permission to open one of the + /// directory components of the specified path. + /// * [`AlreadyExists`]: `create_new` was specified and the file already + /// exists. + /// * [`InvalidInput`]: Invalid combinations of open options (truncate + /// without write access, create without write or append access, + /// no access mode set, etc.). + /// + /// The following errors don't match any existing [`io::ErrorKind`] at the moment: + /// * One of the directory components of the specified file path + /// was not, in fact, a directory. + /// * Filesystem-level errors: full disk, write permission + /// requested on a read-only file system, exceeded disk quota, too many + /// open files, too long filename, too many symbolic links in the + /// specified path (Unix-like systems only), etc. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// + /// let file = OpenOptions::new().read(true).open("foo.txt"); + /// ``` + /// + /// [`AlreadyExists`]: io::ErrorKind::AlreadyExists + /// [`InvalidInput`]: io::ErrorKind::InvalidInput + /// [`NotFound`]: io::ErrorKind::NotFound + /// [`PermissionDenied`]: io::ErrorKind::PermissionDenied + #[stable(feature = "rust1", since = "1.0.0")] + pub fn open>(&self, path: P) -> io::Result { + self._open(path.as_ref()) + } + + fn _open(&self, path: &Path) -> io::Result { + fs_imp::File::open(path, &self.0).map(|inner| File { inner }) + } +} + +impl AsInner for OpenOptions { + #[inline] + fn as_inner(&self) -> &fs_imp::OpenOptions { + &self.0 + } +} + +impl AsInnerMut for OpenOptions { + #[inline] + fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { + &mut self.0 + } +} + +impl Metadata { + /// Returns the file type for this metadata. + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// + /// println!("{:?}", metadata.file_type()); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "file_type", since = "1.1.0")] + pub fn file_type(&self) -> FileType { + FileType(self.0.file_type()) + } + + /// Returns `true` if this metadata is for a directory. The + /// result is mutually exclusive to the result of + /// [`Metadata::is_file`], and will be false for symlink metadata + /// obtained from [`symlink_metadata`]. + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert!(!metadata.is_dir()); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_dir(&self) -> bool { + self.file_type().is_dir() + } + + /// Returns `true` if this metadata is for a regular file. The + /// result is mutually exclusive to the result of + /// [`Metadata::is_dir`], and will be false for symlink metadata + /// obtained from [`symlink_metadata`]. + /// + /// When the goal is simply to read from (or write to) the source, the most + /// reliable way to test the source can be read (or written to) is to open + /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on + /// a Unix-like system for example. See [`File::open`] or + /// [`OpenOptions::open`] for more information. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert!(metadata.is_file()); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_file(&self) -> bool { + self.file_type().is_file() + } + + /// Returns `true` if this metadata is for a symbolic link. + /// + /// # Examples + /// + #[cfg_attr(unix, doc = "```no_run")] + #[cfg_attr(not(unix), doc = "```ignore")] + /// use std::fs; + /// use std::path::Path; + /// use std::os::unix::fs::symlink; + /// + /// fn main() -> std::io::Result<()> { + /// let link_path = Path::new("link"); + /// symlink("/origin_does_not_exist/", link_path)?; + /// + /// let metadata = fs::symlink_metadata(link_path)?; + /// + /// assert!(metadata.is_symlink()); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "is_symlink", since = "1.58.0")] + pub fn is_symlink(&self) -> bool { + self.file_type().is_symlink() + } + + /// Returns the size of the file, in bytes, this metadata is for. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert_eq!(0, metadata.len()); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> u64 { + self.0.size() + } + + /// Returns the permissions of the file this metadata is for. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// assert!(!metadata.permissions().readonly()); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn permissions(&self) -> Permissions { + Permissions(self.0.perm()) + } + + /// Returns the last modification time listed in this metadata. + /// + /// The returned value corresponds to the `mtime` field of `stat` on Unix + /// platforms and the `ftLastWriteTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field might not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// if let Ok(time) = metadata.modified() { + /// println!("{time:?}"); + /// } else { + /// println!("Not supported on this platform"); + /// } + /// Ok(()) + /// } + /// ``` + #[doc(alias = "mtime", alias = "ftLastWriteTime")] + #[stable(feature = "fs_time", since = "1.10.0")] + pub fn modified(&self) -> io::Result { + self.0.modified().map(FromInner::from_inner) + } + + /// Returns the last access time of this metadata. + /// + /// The returned value corresponds to the `atime` field of `stat` on Unix + /// platforms and the `ftLastAccessTime` field on Windows platforms. + /// + /// Note that not all platforms will keep this field update in a file's + /// metadata, for example Windows has an option to disable updating this + /// time when files are accessed and Linux similarly has `noatime`. + /// + /// # Errors + /// + /// This field might not be available on all platforms, and will return an + /// `Err` on platforms where it is not available. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// if let Ok(time) = metadata.accessed() { + /// println!("{time:?}"); + /// } else { + /// println!("Not supported on this platform"); + /// } + /// Ok(()) + /// } + /// ``` + #[doc(alias = "atime", alias = "ftLastAccessTime")] + #[stable(feature = "fs_time", since = "1.10.0")] + pub fn accessed(&self) -> io::Result { + self.0.accessed().map(FromInner::from_inner) + } + + /// Returns the creation time listed in this metadata. + /// + /// The returned value corresponds to the `btime` field of `statx` on + /// Linux kernel starting from to 4.11, the `birthtime` field of `stat` on other + /// Unix platforms, and the `ftCreationTime` field on Windows platforms. + /// + /// # Errors + /// + /// This field might not be available on all platforms, and will return an + /// `Err` on platforms or filesystems where it is not available. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::metadata("foo.txt")?; + /// + /// if let Ok(time) = metadata.created() { + /// println!("{time:?}"); + /// } else { + /// println!("Not supported on this platform or filesystem"); + /// } + /// Ok(()) + /// } + /// ``` + #[doc(alias = "btime", alias = "birthtime", alias = "ftCreationTime")] + #[stable(feature = "fs_time", since = "1.10.0")] + pub fn created(&self) -> io::Result { + self.0.created().map(FromInner::from_inner) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Metadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug = f.debug_struct("Metadata"); + debug.field("file_type", &self.file_type()); + debug.field("permissions", &self.permissions()); + debug.field("len", &self.len()); + if let Ok(modified) = self.modified() { + debug.field("modified", &modified); + } + if let Ok(accessed) = self.accessed() { + debug.field("accessed", &accessed); + } + if let Ok(created) = self.created() { + debug.field("created", &created); + } + debug.finish_non_exhaustive() + } +} + +impl AsInner for Metadata { + #[inline] + fn as_inner(&self) -> &fs_imp::FileAttr { + &self.0 + } +} + +impl FromInner for Metadata { + fn from_inner(attr: fs_imp::FileAttr) -> Metadata { + Metadata(attr) + } +} + +impl FileTimes { + /// Creates a new `FileTimes` with no times set. + /// + /// Using the resulting `FileTimes` in [`File::set_times`] will not modify any timestamps. + #[stable(feature = "file_set_times", since = "1.75.0")] + pub fn new() -> Self { + Self::default() + } + + /// Set the last access time of a file. + #[stable(feature = "file_set_times", since = "1.75.0")] + pub fn set_accessed(mut self, t: SystemTime) -> Self { + self.0.set_accessed(t.into_inner()); + self + } + + /// Set the last modified time of a file. + #[stable(feature = "file_set_times", since = "1.75.0")] + pub fn set_modified(mut self, t: SystemTime) -> Self { + self.0.set_modified(t.into_inner()); + self + } +} + +impl AsInnerMut for FileTimes { + fn as_inner_mut(&mut self) -> &mut fs_imp::FileTimes { + &mut self.0 + } +} + +// For implementing OS extension traits in `std::os` +#[stable(feature = "file_set_times", since = "1.75.0")] +impl Sealed for FileTimes {} + +impl Permissions { + /// Returns `true` if these permissions describe a readonly (unwritable) file. + /// + /// # Note + /// + /// This function does not take Access Control Lists (ACLs), Unix group + /// membership and other nuances into account. + /// Therefore the return value of this function cannot be relied upon + /// to predict whether attempts to read or write the file will actually succeed. + /// + /// # Windows + /// + /// On Windows this returns [`FILE_ATTRIBUTE_READONLY`](https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants). + /// If `FILE_ATTRIBUTE_READONLY` is set then writes to the file will fail + /// but the user may still have permission to change this flag. If + /// `FILE_ATTRIBUTE_READONLY` is *not* set then writes may still fail due + /// to lack of write permission. + /// The behavior of this attribute for directories depends on the Windows + /// version. + /// + /// # Unix (including macOS) + /// + /// On Unix-based platforms this checks if *any* of the owner, group or others + /// write permission bits are set. It does not consider anything else, including: + /// + /// * Whether the current user is in the file's assigned group. + /// * Permissions granted by ACL. + /// * That `root` user can write to files that do not have any write bits set. + /// * Writable files on a filesystem that is mounted read-only. + /// + /// The [`PermissionsExt`] trait gives direct access to the permission bits but + /// also does not read ACLs. + /// + /// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// + /// assert_eq!(false, metadata.permissions().readonly()); + /// Ok(()) + /// } + /// ``` + #[must_use = "call `set_readonly` to modify the readonly flag"] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn readonly(&self) -> bool { + self.0.readonly() + } + + /// Modifies the readonly flag for this set of permissions. If the + /// `readonly` argument is `true`, using the resulting `Permission` will + /// update file permissions to forbid writing. Conversely, if it's `false`, + /// using the resulting `Permission` will update file permissions to allow + /// writing. + /// + /// This operation does **not** modify the files attributes. This only + /// changes the in-memory value of these attributes for this `Permissions` + /// instance. To modify the files attributes use the [`set_permissions`] + /// function which commits these attribute changes to the file. + /// + /// # Note + /// + /// `set_readonly(false)` makes the file *world-writable* on Unix. + /// You can use the [`PermissionsExt`] trait on Unix to avoid this issue. + /// + /// It also does not take Access Control Lists (ACLs) or Unix group + /// membership into account. + /// + /// # Windows + /// + /// On Windows this sets or clears [`FILE_ATTRIBUTE_READONLY`](https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants). + /// If `FILE_ATTRIBUTE_READONLY` is set then writes to the file will fail + /// but the user may still have permission to change this flag. If + /// `FILE_ATTRIBUTE_READONLY` is *not* set then the write may still fail if + /// the user does not have permission to write to the file. + /// + /// In Windows 7 and earlier this attribute prevents deleting empty + /// directories. It does not prevent modifying the directory contents. + /// On later versions of Windows this attribute is ignored for directories. + /// + /// # Unix (including macOS) + /// + /// On Unix-based platforms this sets or clears the write access bit for + /// the owner, group *and* others, equivalent to `chmod a+w ` + /// or `chmod a-w ` respectively. The latter will grant write access + /// to all users! You can use the [`PermissionsExt`] trait on Unix + /// to avoid this issue. + /// + /// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let mut permissions = metadata.permissions(); + /// + /// permissions.set_readonly(true); + /// + /// // filesystem doesn't change, only the in memory state of the + /// // readonly permission + /// assert_eq!(false, metadata.permissions().readonly()); + /// + /// // just this particular `permissions`. + /// assert_eq!(true, permissions.readonly()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_readonly(&mut self, readonly: bool) { + self.0.set_readonly(readonly) + } +} + +impl FileType { + /// Tests whether this file type represents a directory. The + /// result is mutually exclusive to the results of + /// [`is_file`] and [`is_symlink`]; only zero or one of these + /// tests may pass. + /// + /// [`is_file`]: FileType::is_file + /// [`is_symlink`]: FileType::is_symlink + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// let file_type = metadata.file_type(); + /// + /// assert_eq!(file_type.is_dir(), false); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "file_type", since = "1.1.0")] + pub fn is_dir(&self) -> bool { + self.0.is_dir() + } + + /// Tests whether this file type represents a regular file. + /// The result is mutually exclusive to the results of + /// [`is_dir`] and [`is_symlink`]; only zero or one of these + /// tests may pass. + /// + /// When the goal is simply to read from (or write to) the source, the most + /// reliable way to test the source can be read (or written to) is to open + /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on + /// a Unix-like system for example. See [`File::open`] or + /// [`OpenOptions::open`] for more information. + /// + /// [`is_dir`]: FileType::is_dir + /// [`is_symlink`]: FileType::is_symlink + /// + /// # Examples + /// + /// ```no_run + /// fn main() -> std::io::Result<()> { + /// use std::fs; + /// + /// let metadata = fs::metadata("foo.txt")?; + /// let file_type = metadata.file_type(); + /// + /// assert_eq!(file_type.is_file(), true); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "file_type", since = "1.1.0")] + pub fn is_file(&self) -> bool { + self.0.is_file() + } + + /// Tests whether this file type represents a symbolic link. + /// The result is mutually exclusive to the results of + /// [`is_dir`] and [`is_file`]; only zero or one of these + /// tests may pass. + /// + /// The underlying [`Metadata`] struct needs to be retrieved + /// with the [`fs::symlink_metadata`] function and not the + /// [`fs::metadata`] function. The [`fs::metadata`] function + /// follows symbolic links, so [`is_symlink`] would always + /// return `false` for the target file. + /// + /// [`fs::metadata`]: metadata + /// [`fs::symlink_metadata`]: symlink_metadata + /// [`is_dir`]: FileType::is_dir + /// [`is_file`]: FileType::is_file + /// [`is_symlink`]: FileType::is_symlink + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// let metadata = fs::symlink_metadata("foo.txt")?; + /// let file_type = metadata.file_type(); + /// + /// assert_eq!(file_type.is_symlink(), false); + /// Ok(()) + /// } + /// ``` + #[must_use] + #[stable(feature = "file_type", since = "1.1.0")] + pub fn is_symlink(&self) -> bool { + self.0.is_symlink() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FileType") + .field("is_file", &self.is_file()) + .field("is_dir", &self.is_dir()) + .field("is_symlink", &self.is_symlink()) + .finish_non_exhaustive() + } +} + +impl AsInner for FileType { + #[inline] + fn as_inner(&self) -> &fs_imp::FileType { + &self.0 + } +} + +impl FromInner for Permissions { + fn from_inner(f: fs_imp::FilePermissions) -> Permissions { + Permissions(f) + } +} + +impl AsInner for Permissions { + #[inline] + fn as_inner(&self) -> &fs_imp::FilePermissions { + &self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0.next().map(|entry| entry.map(DirEntry)) + } +} + +impl DirEntry { + /// Returns the full path to the file that this entry represents. + /// + /// The full path is created by joining the original path to `read_dir` + /// with the filename of this entry. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// + /// fn main() -> std::io::Result<()> { + /// for entry in fs::read_dir(".")? { + /// let dir = entry?; + /// println!("{:?}", dir.path()); + /// } + /// Ok(()) + /// } + /// ``` + /// + /// This prints output like: + /// + /// ```text + /// "./whatever.txt" + /// "./foo.html" + /// "./hello_world.rs" + /// ``` + /// + /// The exact text, of course, depends on what files you have in `.`. + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn path(&self) -> PathBuf { + self.0.path() + } + + /// Returns the metadata for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. To traverse symlinks use [`fs::metadata`] or [`fs::File::metadata`]. + /// + /// [`fs::metadata`]: metadata + /// [`fs::File::metadata`]: File::metadata + /// + /// # Platform-specific behavior + /// + /// On Windows this function is cheap to call (no extra system calls + /// needed), but on Unix platforms this function is the equivalent of + /// calling `symlink_metadata` on the path. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// if let Ok(metadata) = entry.metadata() { + /// // Now let's show our entry's permissions! + /// println!("{:?}: {:?}", entry.path(), metadata.permissions()); + /// } else { + /// println!("Couldn't get metadata for {:?}", entry.path()); + /// } + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + pub fn metadata(&self) -> io::Result { + self.0.metadata().map(Metadata) + } + + /// Returns the file type for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + /// + /// # Platform-specific behavior + /// + /// On Windows and most Unix platforms this function is free (no extra + /// system calls needed), but some Unix platforms may require the equivalent + /// call to `symlink_metadata` to learn about the target file type. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// if let Ok(file_type) = entry.file_type() { + /// // Now let's show our entry's file type! + /// println!("{:?}: {:?}", entry.path(), file_type); + /// } else { + /// println!("Couldn't get file type for {:?}", entry.path()); + /// } + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + pub fn file_type(&self) -> io::Result { + self.0.file_type().map(FileType) + } + + /// Returns the file name of this directory entry without any + /// leading path component(s). + /// + /// As an example, + /// the output of the function will result in "foo" for all the following paths: + /// - "./foo" + /// - "/the/foo" + /// - "../../foo" + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// println!("{:?}", entry.file_name()); + /// } + /// } + /// } + /// ``` + #[must_use] + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + pub fn file_name(&self) -> OsString { + self.0.file_name() + } +} + +#[stable(feature = "dir_entry_debug", since = "1.13.0")] +impl fmt::Debug for DirEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DirEntry").field(&self.path()).finish() + } +} + +impl AsInner for DirEntry { + #[inline] + fn as_inner(&self) -> &fs_imp::DirEntry { + &self.0 + } +} + +/// Removes a file from the filesystem. +/// +/// Note that there is no +/// guarantee that the file is immediately deleted (e.g., depending on +/// platform, other open file descriptors may prevent immediate removal). +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `unlink` function on Unix. +/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` points to a directory. +/// * The file doesn't exist. +/// * The user lacks permissions to remove the file. +/// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::remove_file("a.txt")?; +/// Ok(()) +/// } +/// ``` +#[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn remove_file>(path: P) -> io::Result<()> { + fs_imp::remove_file(path.as_ref()) +} + +/// Given a path, queries the file system to get information about a file, +/// directory, etc. +/// +/// This function will traverse symbolic links to query information about the +/// destination file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `stat` function on Unix +/// and the `GetFileInformationByHandle` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The user lacks permissions to perform `metadata` call on `path`. +/// * `path` does not exist. +/// +/// # Examples +/// +/// ```rust,no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let attr = fs::metadata("/some/file/path.txt")?; +/// // inspect attr ... +/// Ok(()) +/// } +/// ``` +#[doc(alias = "stat")] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn metadata>(path: P) -> io::Result { + fs_imp::metadata(path.as_ref()).map(Metadata) +} + +/// Queries the metadata about a file without following symlinks. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `lstat` function on Unix +/// and the `GetFileInformationByHandle` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The user lacks permissions to perform `metadata` call on `path`. +/// * `path` does not exist. +/// +/// # Examples +/// +/// ```rust,no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let attr = fs::symlink_metadata("/some/file/path.txt")?; +/// // inspect attr ... +/// Ok(()) +/// } +/// ``` +#[doc(alias = "lstat")] +#[stable(feature = "symlink_metadata", since = "1.1.0")] +pub fn symlink_metadata>(path: P) -> io::Result { + fs_imp::symlink_metadata(path.as_ref()).map(Metadata) +} + +/// Renames a file or directory to a new name, replacing the original file if +/// `to` already exists. +/// +/// This will not work if the new name is on a different mount point. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `rename` function on Unix +/// and the `MoveFileExW` or `SetFileInformationByHandle` function on Windows. +/// +/// Because of this, the behavior when both `from` and `to` exist differs. On +/// Unix, if `from` is a directory, `to` must also be an (empty) directory. If +/// `from` is not a directory, `to` must also be not a directory. The behavior +/// on Windows is the same on Windows 10 1607 and higher if `FileRenameInfoEx` +/// is supported by the filesystem; otherwise, `from` can be anything, but +/// `to` must *not* be a directory. +/// +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `from` does not exist. +/// * The user lacks permissions to view contents. +/// * `from` and `to` are on separate filesystems. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::rename("a.txt", "b.txt")?; // Rename a.txt to b.txt +/// Ok(()) +/// } +/// ``` +#[doc(alias = "mv", alias = "MoveFile", alias = "MoveFileEx")] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { + fs_imp::rename(from.as_ref(), to.as_ref()) +} + +/// Copies the contents of one file to another. This function will also +/// copy the permission bits of the original file to the destination file. +/// +/// This function will **overwrite** the contents of `to`. +/// +/// Note that if `from` and `to` both point to the same file, then the file +/// will likely get truncated by this operation. +/// +/// On success, the total number of bytes copied is returned and it is equal to +/// the length of the `to` file as reported by `metadata`. +/// +/// If you want to copy the contents of one file to another and you’re +/// working with [`File`]s, see the [`io::copy`](io::copy()) function. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `open` function in Unix +/// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. +/// `O_CLOEXEC` is set for returned file descriptors. +/// +/// On Linux (including Android), this function attempts to use `copy_file_range(2)`, +/// and falls back to reading and writing if that is not possible. +/// +/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate +/// NTFS streams are copied but only the size of the main stream is returned by +/// this function. +/// +/// On MacOS, this function corresponds to `fclonefileat` and `fcopyfile`. +/// +/// Note that platform-specific behavior [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `from` is neither a regular file nor a symlink to a regular file. +/// * `from` does not exist. +/// * The current process does not have the permission rights to read +/// `from` or write `to`. +/// * The parent directory of `to` doesn't exist. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::copy("foo.txt", "bar.txt")?; // Copy foo.txt to bar.txt +/// Ok(()) +/// } +/// ``` +#[doc(alias = "cp")] +#[doc(alias = "CopyFile", alias = "CopyFileEx")] +#[doc(alias = "fclonefileat", alias = "fcopyfile")] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { + fs_imp::copy(from.as_ref(), to.as_ref()) +} + +/// Creates a new hard link on the filesystem. +/// +/// The `link` path will be a link pointing to the `original` path. Note that +/// systems often require these two paths to both be located on the same +/// filesystem. +/// +/// If `original` names a symbolic link, it is platform-specific whether the +/// symbolic link is followed. On platforms where it's possible to not follow +/// it, it is not followed, and the created hard link points to the symbolic +/// link itself. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds the `CreateHardLink` function on Windows. +/// On most Unix systems, it corresponds to the `linkat` function with no flags. +/// On Android, VxWorks, and Redox, it instead corresponds to the `link` function. +/// On MacOS, it uses the `linkat` function if it is available, but on very old +/// systems where `linkat` is not available, `link` is selected at runtime instead. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The `original` path is not a file or doesn't exist. +/// * The 'link' path already exists. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::hard_link("a.txt", "b.txt")?; // Hard link a.txt to b.txt +/// Ok(()) +/// } +/// ``` +#[doc(alias = "CreateHardLink", alias = "linkat")] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn hard_link, Q: AsRef>(original: P, link: Q) -> io::Result<()> { + fs_imp::hard_link(original.as_ref(), link.as_ref()) +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `link` path will be a symbolic link pointing to the `original` path. +/// On Windows, this will be a file symlink, not a directory symlink; +/// for this reason, the platform-specific [`std::os::unix::fs::symlink`] +/// and [`std::os::windows::fs::symlink_file`] or [`symlink_dir`] should be +/// used instead to make the intent explicit. +/// +/// [`std::os::unix::fs::symlink`]: crate::os::unix::fs::symlink +/// [`std::os::windows::fs::symlink_file`]: crate::os::windows::fs::symlink_file +/// [`symlink_dir`]: crate::os::windows::fs::symlink_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::soft_link("a.txt", "b.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[deprecated( + since = "1.1.0", + note = "replaced with std::os::unix::fs::symlink and \ + std::os::windows::fs::{symlink_file, symlink_dir}" +)] +pub fn soft_link, Q: AsRef>(original: P, link: Q) -> io::Result<()> { + fs_imp::symlink(original.as_ref(), link.as_ref()) +} + +/// Reads a symbolic link, returning the file that the link points to. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `readlink` function on Unix +/// and the `CreateFile` function with `FILE_FLAG_OPEN_REPARSE_POINT` and +/// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` is not a symbolic link. +/// * `path` does not exist. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let path = fs::read_link("a.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn read_link>(path: P) -> io::Result { + fs_imp::read_link(path.as_ref()) +} + +/// Returns the canonical, absolute form of a path with all intermediate +/// components normalized and symbolic links resolved. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `realpath` function on Unix +/// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. +/// Note that this [may change in the future][changes]. +/// +/// On Windows, this converts the path to use [extended length path][path] +/// syntax, which allows your program to use longer path names, but means you +/// can only join backslash-delimited paths to it, and it may be incompatible +/// with other applications (if passed to the application on the command-line, +/// or written to a file another application may read). +/// +/// [changes]: io#platform-specific-behavior +/// [path]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * A non-final component in path is not a directory. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let path = fs::canonicalize("../a/../foo.txt")?; +/// Ok(()) +/// } +/// ``` +#[doc(alias = "realpath")] +#[doc(alias = "GetFinalPathNameByHandle")] +#[stable(feature = "fs_canonicalize", since = "1.5.0")] +pub fn canonicalize>(path: P) -> io::Result { + fs_imp::canonicalize(path.as_ref()) +} + +/// Creates a new, empty directory at the provided path. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `mkdir` function on Unix +/// and the `CreateDirectoryW` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// **NOTE**: If a parent of the given path doesn't exist, this function will +/// return an error. To create a directory and all its missing parents at the +/// same time, use the [`create_dir_all`] function. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * User lacks permissions to create directory at `path`. +/// * A parent of the given path doesn't exist. (To create a directory and all +/// its missing parents at the same time, use the [`create_dir_all`] +/// function.) +/// * `path` already exists. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[doc(alias = "mkdir", alias = "CreateDirectory")] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "fs_create_dir")] +pub fn create_dir>(path: P) -> io::Result<()> { + DirBuilder::new().create(path.as_ref()) +} + +/// Recursively create a directory and all of its parent components if they +/// are missing. +/// +/// This function is not atomic. If it returns an error, any parent components it was able to create +/// will remain. +/// +/// If the empty path is passed to this function, it always succeeds without +/// creating any directories. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to multiple calls to the `mkdir` +/// function on Unix and the `CreateDirectoryW` function on Windows. +/// +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// The function will return an error if any directory specified in path does not exist and +/// could not be created. There may be other error conditions; see [`fs::create_dir`] for specifics. +/// +/// Notable exception is made for situations where any of the directories +/// specified in the `path` could not be created as it was being created concurrently. +/// Such cases are considered to be successful. That is, calling `create_dir_all` +/// concurrently from multiple threads or processes is guaranteed not to fail +/// due to a race condition with itself. +/// +/// [`fs::create_dir`]: create_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::create_dir_all("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn create_dir_all>(path: P) -> io::Result<()> { + DirBuilder::new().recursive(true).create(path.as_ref()) +} + +/// Removes an empty directory. +/// +/// If you want to remove a directory that is not empty, as well as all +/// of its contents recursively, consider using [`remove_dir_all`] +/// instead. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `rmdir` function on Unix +/// and the `RemoveDirectory` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` doesn't exist. +/// * `path` isn't a directory. +/// * The user lacks permissions to remove the directory at the provided `path`. +/// * The directory isn't empty. +/// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::remove_dir("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[doc(alias = "rmdir", alias = "RemoveDirectory")] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn remove_dir>(path: P) -> io::Result<()> { + fs_imp::remove_dir(path.as_ref()) +} + +/// Removes a directory at this path, after removing all its contents. Use +/// carefully! +/// +/// This function does **not** follow symbolic links and it will simply remove the +/// symbolic link itself. +/// +/// # Platform-specific behavior +/// +/// These implementation details [may change in the future][changes]. +/// +/// - "Unix-like": By default, this function currently corresponds to +/// `openat`, `fdopendir`, `unlinkat` and `lstat` +/// on Unix-family platforms, except where noted otherwise. +/// - "Windows": This function currently corresponds to `CreateFileW`, +/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile`. +/// +/// ## Time-of-check to time-of-use (TOCTOU) race conditions +/// See the [module-level TOCTOU explanation](self#time-of-check-to-time-of-use-toctou). +/// +/// On most platforms, `fs::remove_dir_all` protects against symlink TOCTOU races by default. +/// However, on the following platforms, this protection is not provided and the function should +/// not be used in security-sensitive contexts: +/// - **Miri**: Even when emulating targets where the underlying implementation will protect against +/// TOCTOU races, Miri will not do so. +/// - **Redox OS**: This function does not protect against TOCTOU races, as Redox does not implement +/// the required platform support to do so. +/// +/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// See [`fs::remove_file`] and [`fs::remove_dir`]. +/// +/// [`remove_dir_all`] will fail if [`remove_dir`] or [`remove_file`] fail on *any* constituent +/// paths, *including* the root `path`. Consequently, +/// +/// - The directory you are deleting *must* exist, meaning that this function is *not idempotent*. +/// - [`remove_dir_all`] will fail if the `path` is *not* a directory. +/// +/// Consider ignoring the error if validating the removal is not required for your use case. +/// +/// This function may return [`io::ErrorKind::DirectoryNotEmpty`] if the directory is concurrently +/// written into, which typically indicates some contents were removed but not all. +/// [`io::ErrorKind::NotFound`] is only returned if no removal occurs. +/// +/// [`fs::remove_file`]: remove_file +/// [`fs::remove_dir`]: remove_dir +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::remove_dir_all("/some/dir")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn remove_dir_all>(path: P) -> io::Result<()> { + fs_imp::remove_dir_all(path.as_ref()) +} + +/// Returns an iterator over the entries within a directory. +/// +/// The iterator will yield instances of [io::Result]<[DirEntry]>. +/// New errors may be encountered after an iterator is initially constructed. +/// Entries for the current and parent directories (typically `.` and `..`) are +/// skipped. +/// +/// The order in which `read_dir` returns entries can change between calls. If reproducible +/// ordering is required, the entries should be explicitly sorted. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `opendir` function on Unix +/// and the `FindFirstFileEx` function on Windows. Advancing the iterator +/// currently corresponds to `readdir` on Unix and `FindNextFile` on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// The order in which this iterator returns entries is platform and filesystem +/// dependent. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The provided `path` doesn't exist. +/// * The process lacks permissions to view the contents. +/// * The `path` points at a non-directory file. +/// +/// # Examples +/// +/// ``` +/// use std::io; +/// use std::fs::{self, DirEntry}; +/// use std::path::Path; +/// +/// // one possible implementation of walking a directory only visiting files +/// fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> { +/// if dir.is_dir() { +/// for entry in fs::read_dir(dir)? { +/// let entry = entry?; +/// let path = entry.path(); +/// if path.is_dir() { +/// visit_dirs(&path, cb)?; +/// } else { +/// cb(&entry); +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +/// +/// ```rust,no_run +/// use std::{fs, io}; +/// +/// fn main() -> io::Result<()> { +/// let mut entries = fs::read_dir(".")? +/// .map(|res| res.map(|e| e.path())) +/// .collect::, io::Error>>()?; +/// +/// // The order in which `read_dir` returns entries is not guaranteed. If reproducible +/// // ordering is required the entries should be explicitly sorted. +/// +/// entries.sort(); +/// +/// // The entries have now been sorted by their path. +/// +/// Ok(()) +/// } +/// ``` +#[doc(alias = "ls", alias = "opendir", alias = "FindFirstFile", alias = "FindNextFile")] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn read_dir>(path: P) -> io::Result { + fs_imp::read_dir(path.as_ref()).map(ReadDir) +} + +/// Changes the permissions found on a file or a directory. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `chmod` function on Unix +/// and the `SetFileAttributes` function on Windows. +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// +/// ## Symlinks +/// On UNIX-like systems, this function will update the permission bits +/// of the file pointed to by the symlink. +/// +/// Note that this behavior can lead to privilege escalation vulnerabilities, +/// where the ability to create a symlink in one directory allows you to +/// cause the permissions of another file or directory to be modified. +/// +/// For this reason, using this function with symlinks should be avoided. +/// When possible, permissions should be set at creation time instead. +/// +/// # Rationale +/// POSIX does not specify an `lchmod` function, +/// and symlinks can be followed regardless of what permission bits are set. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * The user lacks the permission to change attributes of the file. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let mut perms = fs::metadata("foo.txt")?.permissions(); +/// perms.set_readonly(true); +/// fs::set_permissions("foo.txt", perms)?; +/// Ok(()) +/// } +/// ``` +#[doc(alias = "chmod", alias = "SetFileAttributes")] +#[stable(feature = "set_permissions", since = "1.1.0")] +pub fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { + fs_imp::set_permissions(path.as_ref(), perm.0) +} + +/// Set the permissions of a file, unless it is a symlink. +/// +/// Note that the non-final path elements are allowed to be symlinks. +/// +/// # Platform-specific behavior +/// +/// Currently unimplemented on Windows. +/// +/// On Unix platforms, this results in a [`FilesystemLoop`] error if the last element is a symlink. +/// +/// This behavior may change in the future. +/// +/// [`FilesystemLoop`]: crate::io::ErrorKind::FilesystemLoop +#[doc(alias = "chmod", alias = "SetFileAttributes")] +#[unstable(feature = "set_permissions_nofollow", issue = "141607")] +pub fn set_permissions_nofollow>(path: P, perm: Permissions) -> io::Result<()> { + fs_imp::set_permissions_nofollow(path.as_ref(), perm) +} + +impl DirBuilder { + /// Creates a new set of options with default mode/security settings for all + /// platforms and also non-recursive. + /// + /// # Examples + /// + /// ``` + /// use std::fs::DirBuilder; + /// + /// let builder = DirBuilder::new(); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + #[must_use] + pub fn new() -> DirBuilder { + DirBuilder { inner: fs_imp::DirBuilder::new(), recursive: false } + } + + /// Indicates that directories should be created recursively, creating all + /// parent directories. Parents that do not exist are created with the same + /// security and permissions settings. + /// + /// This option defaults to `false`. + /// + /// # Examples + /// + /// ``` + /// use std::fs::DirBuilder; + /// + /// let mut builder = DirBuilder::new(); + /// builder.recursive(true); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + pub fn recursive(&mut self, recursive: bool) -> &mut Self { + self.recursive = recursive; + self + } + + /// Creates the specified directory with the options configured in this + /// builder. + /// + /// It is considered an error if the directory already exists unless + /// recursive mode is enabled. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::{self, DirBuilder}; + /// + /// let path = "/tmp/foo/bar/baz"; + /// DirBuilder::new() + /// .recursive(true) + /// .create(path).unwrap(); + /// + /// assert!(fs::metadata(path).unwrap().is_dir()); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + pub fn create>(&self, path: P) -> io::Result<()> { + self._create(path.as_ref()) + } + + fn _create(&self, path: &Path) -> io::Result<()> { + if self.recursive { self.create_dir_all(path) } else { self.inner.mkdir(path) } + } + + fn create_dir_all(&self, path: &Path) -> io::Result<()> { + // if path's parent is None, it is "/" path, which should + // return Ok immediately + if path == Path::new("") || path.parent() == None { + return Ok(()); + } + + let ancestors = path.ancestors(); + let mut uncreated_dirs = 0; + + for ancestor in ancestors { + // for relative paths like "foo/bar", the parent of + // "foo" will be "" which there's no need to invoke + // a mkdir syscall on + if ancestor == Path::new("") || ancestor.parent() == None { + break; + } + + match self.inner.mkdir(ancestor) { + Ok(()) => break, + Err(e) if e.kind() == io::ErrorKind::NotFound => uncreated_dirs += 1, + // we check if the err is AlreadyExists for two reasons + // - in case the path exists as a *file* + // - and to avoid calls to .is_dir() in case of other errs + // (i.e. PermissionDenied) + Err(e) if e.kind() == io::ErrorKind::AlreadyExists && ancestor.is_dir() => break, + Err(e) => return Err(e), + } + } + + // collect only the uncreated directories w/o letting the vec resize + let mut uncreated_dirs_vec = Vec::with_capacity(uncreated_dirs); + uncreated_dirs_vec.extend(ancestors.take(uncreated_dirs)); + + for uncreated_dir in uncreated_dirs_vec.iter().rev() { + if let Err(e) = self.inner.mkdir(uncreated_dir) { + if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { + return Err(e); + } + } + } + + Ok(()) + } +} + +impl AsInnerMut for DirBuilder { + #[inline] + fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { + &mut self.inner + } +} + +/// Returns `Ok(true)` if the path points at an existing entity. +/// +/// This function will traverse symbolic links to query information about the +/// destination file. In case of broken symbolic links this will return `Ok(false)`. +/// +/// As opposed to the [`Path::exists`] method, this will only return `Ok(true)` or `Ok(false)` +/// if the path was _verified_ to exist or not exist. If its existence can neither be confirmed +/// nor denied, an `Err(_)` will be propagated instead. This can be the case if e.g. listing +/// permission is denied on one of the parent directories. +/// +/// Note that while this avoids some pitfalls of the `exists()` method, it still can not +/// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios +/// where those bugs are not an issue. +/// +/// # Examples +/// +/// ```no_run +/// use std::fs; +/// +/// assert!(!fs::exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt")); +/// assert!(fs::exists("/root/secret_file.txt").is_err()); +/// ``` +/// +/// [`Path::exists`]: crate::path::Path::exists +/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou +#[stable(feature = "fs_try_exists", since = "1.81.0")] +#[inline] +pub fn exists>(path: P) -> io::Result { + fs_imp::exists(path.as_ref()) +} diff --git a/crates/std/src/io.rs b/crates/std/src/io.rs index 74e35a2..2a2fdf5 100644 --- a/crates/std/src/io.rs +++ b/crates/std/src/io.rs @@ -21,7 +21,7 @@ pub fn stdin() -> Stdin { Stdin } -pub type Result = core::result::Result; +pub type Result = core::result::Result; pub(super) struct Repr(); @@ -30,10 +30,42 @@ pub(super) struct Repr(); #[unstable(feature = "io_const_error", issue = "133448")] #[allow_internal_unstable(hint_must_use, io_const_error_internals)] pub macro const_error($kind:expr, $message:expr $(,)?) { - () + crate::io::Error::new() } #[stable(feature = "rust1", since = "1.0.0")] pub struct Error { repr: Repr, } + +impl Error { + pub const fn new() -> Self { + Self { repr: Repr() } + } +} + +#[allow(dead_code)] +impl Error { + pub(crate) const INVALID_UTF8: Self = + const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + + pub(crate) const READ_EXACT_EOF: Self = + const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + + pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( + ErrorKind::NotFound, + "the number of hardware threads is not known for the target platform", + ); + + pub(crate) const UNSUPPORTED_PLATFORM: Self = + const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + + pub(crate) const WRITE_ALL_EOF: Self = + const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + + pub(crate) const ZERO_TIMEOUT: Self = + const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + + pub(crate) const NO_ADDRESSES: Self = + const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); +} diff --git a/crates/std/src/lib.rs b/crates/std/src/lib.rs index c3d17ee..0a9e00b 100644 --- a/crates/std/src/lib.rs +++ b/crates/std/src/lib.rs @@ -181,10 +181,9 @@ #![feature(io_const_error)] // tidy-alphabetical-end // - #![feature(c_size_t, unsafe_binders)] #![allow(clippy::doc_lazy_continuation, clippy::all)] -#![allow(stable_features, incomplete_features)] +#![allow(stable_features, incomplete_features, unexpected_cfgs)] #![allow(unused)] extern crate alloc; @@ -246,12 +245,25 @@ pub use alloc::str; pub use alloc::string; pub use alloc::vec; +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +pub use core::{ + assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, +}; + pub mod ffi; pub mod hash; pub mod io; +// pub mod fs; +pub mod error; +pub mod num; +pub mod path; pub mod prelude; pub mod process; pub mod sys; +pub mod time; +pub mod thread; #[prelude_import] #[allow(unused_imports)] diff --git a/crates/std/src/num/mod.rs b/crates/std/src/num/mod.rs new file mode 100644 index 0000000..ffb8789 --- /dev/null +++ b/crates/std/src/num/mod.rs @@ -0,0 +1,28 @@ +//! Additional functionality for numerics. +//! +//! This module provides some extra types that are useful when doing numerical +//! work. See the individual documentation for each piece for more information. + +#![stable(feature = "rust1", since = "1.0.0")] +#![allow(missing_docs)] + +#[stable(feature = "int_error_matching", since = "1.55.0")] +pub use core::num::IntErrorKind; +#[stable(feature = "generic_nonzero", since = "1.79.0")] +pub use core::num::NonZero; +#[stable(feature = "saturating_int_impl", since = "1.74.0")] +pub use core::num::Saturating; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::num::Wrapping; +#[unstable( + feature = "nonzero_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +pub use core::num::ZeroablePrimitive; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError}; +#[stable(feature = "signed_nonzero", since = "1.34.0")] +pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; +#[stable(feature = "nonzero", since = "1.28.0")] +pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; diff --git a/crates/std/src/path.rs b/crates/std/src/path.rs new file mode 100644 index 0000000..0b8b22f --- /dev/null +++ b/crates/std/src/path.rs @@ -0,0 +1,4047 @@ +//! Cross-platform path manipulation. +//! +//! This module provides two types, [`PathBuf`] and [`Path`] (akin to [`String`] +//! and [`str`]), for working with paths abstractly. These types are thin wrappers +//! around [`OsString`] and [`OsStr`] respectively, meaning that they work directly +//! on strings according to the local platform's path syntax. +//! +//! Paths can be parsed into [`Component`]s by iterating over the structure +//! returned by the [`components`] method on [`Path`]. [`Component`]s roughly +//! correspond to the substrings between path separators (`/` or `\`). You can +//! reconstruct an equivalent path from components with the [`push`] method on +//! [`PathBuf`]; note that the paths may differ syntactically by the +//! normalization described in the documentation for the [`components`] method. +//! +//! ## Case sensitivity +//! +//! Unless otherwise indicated path methods that do not access the filesystem, +//! such as [`Path::starts_with`] and [`Path::ends_with`], are case sensitive no +//! matter the platform or filesystem. An exception to this is made for Windows +//! drive letters. +//! +//! ## Path normalization +//! +//! Several methods in this module perform basic path normalization by disregarding +//! repeated separators, non-leading `.` components, and trailing separators. These include: +//! - Methods for iteration, such as [`Path::components`] and [`Path::iter`] +//! - Methods for inspection, such as [`Path::has_root`] +//! - Comparisons using [`PartialEq`], [`PartialOrd`], and [`Ord`] +//! +//! [`Path::join`] and [`PathBuf::push`] also disregard trailing slashes. +//! +// FIXME(normalize_lexically): mention normalize_lexically once stable +//! These methods **do not** resolve `..` components or symlinks. For full normalization +//! including `..` resolution, use [`Path::canonicalize`] (which does access the filesystem). +//! +//! ## Simple usage +//! +//! Path manipulation includes both parsing components from slices and building +//! new owned paths. +//! +//! To parse a path, you can create a [`Path`] slice from a [`str`] +//! slice and start asking questions: +//! +//! ``` +//! use std::path::Path; +//! use std::ffi::OsStr; +//! +//! let path = Path::new("/tmp/foo/bar.txt"); +//! +//! let parent = path.parent(); +//! assert_eq!(parent, Some(Path::new("/tmp/foo"))); +//! +//! let file_stem = path.file_stem(); +//! assert_eq!(file_stem, Some(OsStr::new("bar"))); +//! +//! let extension = path.extension(); +//! assert_eq!(extension, Some(OsStr::new("txt"))); +//! ``` +//! +//! To build or modify paths, use [`PathBuf`]: +//! +//! ``` +//! use std::path::PathBuf; +//! +//! // This way works... +//! let mut path = PathBuf::from("c:\\"); +//! +//! path.push("windows"); +//! path.push("system32"); +//! +//! path.set_extension("dll"); +//! +//! // ... but push is best used if you don't know everything up +//! // front. If you do, this way is better: +//! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect(); +//! ``` +//! +//! [`components`]: Path::components +//! [`push`]: PathBuf::push + +#![stable(feature = "rust1", since = "1.0.0")] +#![deny(unsafe_op_in_unsafe_fn)] + +use core::clone::CloneToUninit; + +use crate::borrow::{Borrow, Cow}; +use alloc::collections::TryReserveError; +use crate::error::Error; +use crate::ffi::{OsStr, OsString, os_str}; +use crate::hash::{Hash, Hasher}; +use crate::iter::FusedIterator; +use crate::ops::{self, Deref}; +use crate::rc::Rc; +use crate::str::FromStr; +use alloc::sync::Arc; +use crate::sys::path::{HAS_PREFIXES, MAIN_SEP_STR, is_sep_byte, is_verbatim_sep, parse_prefix}; +use crate::{cmp, fmt, fs, io, sys}; + +//////////////////////////////////////////////////////////////////////////////// +// GENERAL NOTES +//////////////////////////////////////////////////////////////////////////////// +// +// Parsing in this module is done by directly transmuting OsStr to [u8] slices, +// taking advantage of the fact that OsStr always encodes ASCII characters +// as-is. Eventually, this transmutation should be replaced by direct uses of +// OsStr APIs for parsing, but it will take a while for those to become +// available. + +//////////////////////////////////////////////////////////////////////////////// +// Windows Prefixes +//////////////////////////////////////////////////////////////////////////////// + +/// Windows path prefixes, e.g., `C:` or `\\server\share`. +/// +/// Windows uses a variety of path prefix styles, including references to drive +/// volumes (like `C:`), network shared folders (like `\\server\share`), and +/// others. In addition, some path prefixes are "verbatim" (i.e., prefixed with +/// `\\?\`), in which case `/` is *not* treated as a separator and essentially +/// no normalization is performed. +/// +/// # Examples +/// +/// ``` +/// use std::path::{Component, Path, Prefix}; +/// use std::path::Prefix::*; +/// use std::ffi::OsStr; +/// +/// fn get_path_prefix(s: &str) -> Prefix<'_> { +/// let path = Path::new(s); +/// match path.components().next().unwrap() { +/// Component::Prefix(prefix_component) => prefix_component.kind(), +/// _ => panic!(), +/// } +/// } +/// +/// # if cfg!(windows) { +/// assert_eq!(Verbatim(OsStr::new("pictures")), +/// get_path_prefix(r"\\?\pictures\kittens")); +/// assert_eq!(VerbatimUNC(OsStr::new("server"), OsStr::new("share")), +/// get_path_prefix(r"\\?\UNC\server\share")); +/// assert_eq!(VerbatimDisk(b'C'), get_path_prefix(r"\\?\c:\")); +/// assert_eq!(DeviceNS(OsStr::new("BrainInterface")), +/// get_path_prefix(r"\\.\BrainInterface")); +/// assert_eq!(UNC(OsStr::new("server"), OsStr::new("share")), +/// get_path_prefix(r"\\server\share")); +/// assert_eq!(Disk(b'C'), get_path_prefix(r"C:\Users\Rust\Pictures\Ferris")); +/// # } +/// ``` +#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Prefix<'a> { + /// Verbatim prefix, e.g., `\\?\cat_pics`. + /// + /// Verbatim prefixes consist of `\\?\` immediately followed by the given + /// component. + #[stable(feature = "rust1", since = "1.0.0")] + Verbatim(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), + + /// Verbatim prefix using Windows' _**U**niform **N**aming **C**onvention_, + /// e.g., `\\?\UNC\server\share`. + /// + /// Verbatim UNC prefixes consist of `\\?\UNC\` immediately followed by the + /// server's hostname and a share name. + #[stable(feature = "rust1", since = "1.0.0")] + VerbatimUNC( + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + ), + + /// Verbatim disk prefix, e.g., `\\?\C:`. + /// + /// Verbatim disk prefixes consist of `\\?\` immediately followed by the + /// drive letter and `:`. + #[stable(feature = "rust1", since = "1.0.0")] + VerbatimDisk(#[stable(feature = "rust1", since = "1.0.0")] u8), + + /// Device namespace prefix, e.g., `\\.\COM42`. + /// + /// Device namespace prefixes consist of `\\.\` (possibly using `/` + /// instead of `\`), immediately followed by the device name. + #[stable(feature = "rust1", since = "1.0.0")] + DeviceNS(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), + + /// Prefix using Windows' _**U**niform **N**aming **C**onvention_, e.g. + /// `\\server\share`. + /// + /// UNC prefixes consist of the server's hostname and a share name. + #[stable(feature = "rust1", since = "1.0.0")] + UNC( + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, + ), + + /// Prefix `C:` for the given disk drive. + #[stable(feature = "rust1", since = "1.0.0")] + Disk(#[stable(feature = "rust1", since = "1.0.0")] u8), +} + +impl<'a> Prefix<'a> { + #[inline] + fn len(&self) -> usize { + use self::Prefix::*; + fn os_str_len(s: &OsStr) -> usize { + s.as_encoded_bytes().len() + } + match *self { + Verbatim(x) => 4 + os_str_len(x), + VerbatimUNC(x, y) => { + 8 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 } + } + VerbatimDisk(_) => 6, + UNC(x, y) => 2 + os_str_len(x) + if os_str_len(y) > 0 { 1 + os_str_len(y) } else { 0 }, + DeviceNS(x) => 4 + os_str_len(x), + Disk(_) => 2, + } + } + + /// Determines if the prefix is verbatim, i.e., begins with `\\?\`. + /// + /// # Examples + /// + /// ``` + /// use std::path::Prefix::*; + /// use std::ffi::OsStr; + /// + /// assert!(Verbatim(OsStr::new("pictures")).is_verbatim()); + /// assert!(VerbatimUNC(OsStr::new("server"), OsStr::new("share")).is_verbatim()); + /// assert!(VerbatimDisk(b'C').is_verbatim()); + /// assert!(!DeviceNS(OsStr::new("BrainInterface")).is_verbatim()); + /// assert!(!UNC(OsStr::new("server"), OsStr::new("share")).is_verbatim()); + /// assert!(!Disk(b'C').is_verbatim()); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_verbatim(&self) -> bool { + use self::Prefix::*; + matches!(*self, Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..)) + } + + #[inline] + fn is_drive(&self) -> bool { + matches!(*self, Prefix::Disk(_)) + } + + #[inline] + fn has_implicit_root(&self) -> bool { + !self.is_drive() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Exposed parsing helpers +//////////////////////////////////////////////////////////////////////////////// + +/// Determines whether the character is one of the permitted path +/// separators for the current platform. +/// +/// # Examples +/// +/// ``` +/// use std::path; +/// +/// assert!(path::is_separator('/')); // '/' works for both Unix and Windows +/// assert!(!path::is_separator('❤')); +/// ``` +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn is_separator(c: char) -> bool { + c.is_ascii() && is_sep_byte(c as u8) +} + +/// The primary separator of path components for the current platform. +/// +/// For example, `/` on Unix and `\` on Windows. +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "path_main_separator")] +pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; + +/// The primary separator of path components for the current platform. +/// +/// For example, `/` on Unix and `\` on Windows. +#[stable(feature = "main_separator_str", since = "1.68.0")] +pub const MAIN_SEPARATOR_STR: &str = crate::sys::path::MAIN_SEP_STR; + +//////////////////////////////////////////////////////////////////////////////// +// Misc helpers +//////////////////////////////////////////////////////////////////////////////// + +// Iterate through `iter` while it matches `prefix`; return `None` if `prefix` +// is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving +// `iter` after having exhausted `prefix`. +fn iter_after<'a, 'b, I, J>(mut iter: I, mut prefix: J) -> Option +where + I: Iterator> + Clone, + J: Iterator>, +{ + loop { + let mut iter_next = iter.clone(); + match (iter_next.next(), prefix.next()) { + (Some(ref x), Some(ref y)) if x == y => (), + (Some(_), Some(_)) => return None, + (Some(_), None) => return Some(iter), + (None, None) => return Some(iter), + (None, Some(_)) => return None, + } + iter = iter_next; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Cross-platform, iterator-independent parsing +//////////////////////////////////////////////////////////////////////////////// + +/// Says whether the first byte after the prefix is a separator. +fn has_physical_root(s: &[u8], prefix: Option>) -> bool { + let path = if let Some(p) = prefix { &s[p.len()..] } else { s }; + !path.is_empty() && is_sep_byte(path[0]) +} + +// basic workhorse for splitting stem and extension +fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { + if file.as_encoded_bytes() == b".." { + return (Some(file), None); + } + + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let mut iter = file.as_encoded_bytes().rsplitn(2, |b| *b == b'.'); + let after = iter.next(); + let before = iter.next(); + if before == Some(b"") { + (Some(file), None) + } else { + unsafe { + ( + before.map(|s| OsStr::from_encoded_bytes_unchecked(s)), + after.map(|s| OsStr::from_encoded_bytes_unchecked(s)), + ) + } + } +} + +fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { + let slice = file.as_encoded_bytes(); + if slice == b".." { + return (file, None); + } + + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let i = match slice[1..].iter().position(|b| *b == b'.') { + Some(i) => i + 1, + None => return (file, None), + }; + let before = &slice[..i]; + let after = &slice[i + 1..]; + unsafe { + ( + OsStr::from_encoded_bytes_unchecked(before), + Some(OsStr::from_encoded_bytes_unchecked(after)), + ) + } +} + +/// Checks whether the string is valid as a file extension, or panics otherwise. +fn validate_extension(extension: &OsStr) { + for &b in extension.as_encoded_bytes() { + if is_sep_byte(b) { + panic!("extension cannot contain path separators: {extension:?}"); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// The core iterators +//////////////////////////////////////////////////////////////////////////////// + +/// Component parsing works by a double-ended state machine; the cursors at the +/// front and back of the path each keep track of what parts of the path have +/// been consumed so far. +/// +/// Going front to back, a path is made up of a prefix, a starting +/// directory component, and a body (of normal components) +#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +enum State { + Prefix = 0, // c: + StartDir = 1, // / or . or nothing + Body = 2, // foo/bar/baz + Done = 3, +} + +/// A structure wrapping a Windows path prefix as well as its unparsed string +/// representation. +/// +/// In addition to the parsed [`Prefix`] information returned by [`kind`], +/// `PrefixComponent` also holds the raw and unparsed [`OsStr`] slice, +/// returned by [`as_os_str`]. +/// +/// Instances of this `struct` can be obtained by matching against the +/// [`Prefix` variant] on [`Component`]. +/// +/// Does not occur on Unix. +/// +/// # Examples +/// +/// ``` +/// # if cfg!(windows) { +/// use std::path::{Component, Path, Prefix}; +/// use std::ffi::OsStr; +/// +/// let path = Path::new(r"c:\you\later\"); +/// match path.components().next().unwrap() { +/// Component::Prefix(prefix_component) => { +/// assert_eq!(Prefix::Disk(b'C'), prefix_component.kind()); +/// assert_eq!(OsStr::new("c:"), prefix_component.as_os_str()); +/// } +/// _ => unreachable!(), +/// } +/// # } +/// ``` +/// +/// [`as_os_str`]: PrefixComponent::as_os_str +/// [`kind`]: PrefixComponent::kind +/// [`Prefix` variant]: Component::Prefix +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Copy, Clone, Eq, Debug)] +pub struct PrefixComponent<'a> { + /// The prefix as an unparsed `OsStr` slice. + raw: &'a OsStr, + + /// The parsed prefix data. + parsed: Prefix<'a>, +} + +impl<'a> PrefixComponent<'a> { + /// Returns the parsed prefix data. + /// + /// See [`Prefix`]'s documentation for more information on the different + /// kinds of prefixes. + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn kind(&self) -> Prefix<'a> { + self.parsed + } + + /// Returns the raw [`OsStr`] slice for this prefix. + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn as_os_str(&self) -> &'a OsStr { + self.raw + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> PartialEq for PrefixComponent<'a> { + #[inline] + fn eq(&self, other: &PrefixComponent<'a>) -> bool { + self.parsed == other.parsed + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> PartialOrd for PrefixComponent<'a> { + #[inline] + fn partial_cmp(&self, other: &PrefixComponent<'a>) -> Option { + PartialOrd::partial_cmp(&self.parsed, &other.parsed) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for PrefixComponent<'_> { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + Ord::cmp(&self.parsed, &other.parsed) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for PrefixComponent<'_> { + fn hash(&self, h: &mut H) { + self.parsed.hash(h); + } +} + +/// A single component of a path. +/// +/// A `Component` roughly corresponds to a substring between path separators +/// (`/` or `\`). +/// +/// This `enum` is created by iterating over [`Components`], which in turn is +/// created by the [`components`](Path::components) method on [`Path`]. +/// +/// # Examples +/// +/// ```rust +/// use std::path::{Component, Path}; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// let components = path.components().collect::>(); +/// assert_eq!(&components, &[ +/// Component::RootDir, +/// Component::Normal("tmp".as_ref()), +/// Component::Normal("foo".as_ref()), +/// Component::Normal("bar.txt".as_ref()), +/// ]); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Component<'a> { + /// A Windows path prefix, e.g., `C:` or `\\server\share`. + /// + /// There is a large variety of prefix types, see [`Prefix`]'s documentation + /// for more. + /// + /// Does not occur on Unix. + #[stable(feature = "rust1", since = "1.0.0")] + Prefix(#[stable(feature = "rust1", since = "1.0.0")] PrefixComponent<'a>), + + /// The root directory component, appears after any prefix and before anything else. + /// + /// It represents a separator that designates that a path starts from root. + #[stable(feature = "rust1", since = "1.0.0")] + RootDir, + + /// A reference to the current directory, i.e., `.`. + #[stable(feature = "rust1", since = "1.0.0")] + CurDir, + + /// A reference to the parent directory, i.e., `..`. + #[stable(feature = "rust1", since = "1.0.0")] + ParentDir, + + /// A normal component, e.g., `a` and `b` in `a/b`. + /// + /// This variant is the most common one, it represents references to files + /// or directories. + #[stable(feature = "rust1", since = "1.0.0")] + Normal(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), +} + +impl<'a> Component<'a> { + /// Extracts the underlying [`OsStr`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("./tmp/foo/bar.txt"); + /// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect(); + /// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]); + /// ``` + #[must_use = "`self` will be dropped if the result is not used"] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_os_str(self) -> &'a OsStr { + match self { + Component::Prefix(p) => p.as_os_str(), + Component::RootDir => OsStr::new(MAIN_SEP_STR), + Component::CurDir => OsStr::new("."), + Component::ParentDir => OsStr::new(".."), + Component::Normal(path) => path, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Component<'_> { + #[inline] + fn as_ref(&self) -> &OsStr { + self.as_os_str() + } +} + +#[stable(feature = "path_component_asref", since = "1.25.0")] +impl AsRef for Component<'_> { + #[inline] + fn as_ref(&self) -> &Path { + self.as_os_str().as_ref() + } +} + +/// An iterator over the [`Component`]s of a [`Path`]. +/// +/// This `struct` is created by the [`components`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// +/// for component in path.components() { +/// println!("{component:?}"); +/// } +/// ``` +/// +/// [`components`]: Path::components +#[derive(Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Components<'a> { + // The path left to parse components from + path: &'a [u8], + + // The prefix as it was originally parsed, if any + prefix: Option>, + + // true if path *physically* has a root separator; for most Windows + // prefixes, it may have a "logical" root separator for the purposes of + // normalization, e.g., \\server\share == \\server\share\. + has_physical_root: bool, + + // The iterator is double-ended, and these two states keep track of what has + // been produced from either end + front: State, + back: State, +} + +/// An iterator over the [`Component`]s of a [`Path`], as [`OsStr`] slices. +/// +/// This `struct` is created by the [`iter`] method on [`Path`]. +/// See its documentation for more. +/// +/// [`iter`]: Path::iter +#[derive(Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a> { + inner: Components<'a>, +} + +#[stable(feature = "path_components_debug", since = "1.13.0")] +impl fmt::Debug for Components<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugHelper<'a>(&'a Path); + + impl fmt::Debug for DebugHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.components()).finish() + } + } + + f.debug_tuple("Components").field(&DebugHelper(self.as_path())).finish() + } +} + +impl<'a> Components<'a> { + // how long is the prefix, if any? + #[inline] + fn prefix_len(&self) -> usize { + if !HAS_PREFIXES { + return 0; + } + self.prefix.as_ref().map(Prefix::len).unwrap_or(0) + } + + #[inline] + fn prefix_verbatim(&self) -> bool { + if !HAS_PREFIXES { + return false; + } + self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false) + } + + /// how much of the prefix is left from the point of view of iteration? + #[inline] + fn prefix_remaining(&self) -> usize { + if !HAS_PREFIXES { + return 0; + } + if self.front == State::Prefix { self.prefix_len() } else { 0 } + } + + // Given the iteration so far, how much of the pre-State::Body path is left? + #[inline] + fn len_before_body(&self) -> usize { + let root = if self.front <= State::StartDir && self.has_physical_root { 1 } else { 0 }; + let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() { 1 } else { 0 }; + self.prefix_remaining() + root + cur_dir + } + + // is the iteration complete? + #[inline] + fn finished(&self) -> bool { + self.front == State::Done || self.back == State::Done || self.front > self.back + } + + #[inline] + fn is_sep_byte(&self, b: u8) -> bool { + if self.prefix_verbatim() { is_verbatim_sep(b) } else { is_sep_byte(b) } + } + + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let mut components = Path::new("/tmp/foo/bar.txt").components(); + /// components.next(); + /// components.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), components.as_path()); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn as_path(&self) -> &'a Path { + let mut comps = self.clone(); + if comps.front == State::Body { + comps.trim_left(); + } + if comps.back == State::Body { + comps.trim_right(); + } + unsafe { Path::from_u8_slice(comps.path) } + } + + /// Is the *original* path rooted? + fn has_root(&self) -> bool { + if self.has_physical_root { + return true; + } + if HAS_PREFIXES && let Some(p) = self.prefix { + if p.has_implicit_root() { + return true; + } + } + false + } + + /// Should the normalized path include a leading . ? + fn include_cur_dir(&self) -> bool { + if self.has_root() { + return false; + } + let slice = &self.path[self.prefix_remaining()..]; + match slice { + [b'.'] => true, + [b'.', b, ..] => self.is_sep_byte(*b), + _ => false, + } + } + + // parse a given byte sequence following the OsStr encoding into the + // corresponding path component + unsafe fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option> { + match comp { + b"." if HAS_PREFIXES && self.prefix_verbatim() => Some(Component::CurDir), + b"." => None, // . components are normalized away, except at + // the beginning of a path, which is treated + // separately via `include_cur_dir` + b".." => Some(Component::ParentDir), + b"" => None, + _ => Some(Component::Normal(unsafe { OsStr::from_encoded_bytes_unchecked(comp) })), + } + } + + // parse a component from the left, saying how many bytes to consume to + // remove the component + fn parse_next_component(&self) -> (usize, Option>) { + debug_assert!(self.front == State::Body); + let (extra, comp) = match self.path.iter().position(|b| self.is_sep_byte(*b)) { + None => (0, self.path), + Some(i) => (1, &self.path[..i]), + }; + // SAFETY: `comp` is a valid substring, since it is split on a separator. + (comp.len() + extra, unsafe { self.parse_single_component(comp) }) + } + + // parse a component from the right, saying how many bytes to consume to + // remove the component + fn parse_next_component_back(&self) -> (usize, Option>) { + debug_assert!(self.back == State::Body); + let start = self.len_before_body(); + let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep_byte(*b)) { + None => (0, &self.path[start..]), + Some(i) => (1, &self.path[start + i + 1..]), + }; + // SAFETY: `comp` is a valid substring, since it is split on a separator. + (comp.len() + extra, unsafe { self.parse_single_component(comp) }) + } + + // trim away repeated separators (i.e., empty components) on the left + fn trim_left(&mut self) { + while !self.path.is_empty() { + let (size, comp) = self.parse_next_component(); + if comp.is_some() { + return; + } else { + self.path = &self.path[size..]; + } + } + } + + // trim away repeated separators (i.e., empty components) on the right + fn trim_right(&mut self) { + while self.path.len() > self.len_before_body() { + let (size, comp) = self.parse_next_component_back(); + if comp.is_some() { + return; + } else { + self.path = &self.path[..self.path.len() - size]; + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Components<'_> { + #[inline] + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Components<'_> { + #[inline] + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +#[stable(feature = "path_iter_debug", since = "1.13.0")] +impl fmt::Debug for Iter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugHelper<'a>(&'a Path); + + impl fmt::Debug for DebugHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } + } + + f.debug_tuple("Iter").field(&DebugHelper(self.as_path())).finish() + } +} + +impl<'a> Iter<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let mut iter = Path::new("/tmp/foo/bar.txt").iter(); + /// iter.next(); + /// iter.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn as_path(&self) -> &'a Path { + self.inner.as_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Iter<'_> { + #[inline] + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for Iter<'_> { + #[inline] + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for Iter<'a> { + type Item = &'a OsStr; + + #[inline] + fn next(&mut self) -> Option<&'a OsStr> { + self.inner.next().map(Component::as_os_str) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> DoubleEndedIterator for Iter<'a> { + #[inline] + fn next_back(&mut self) -> Option<&'a OsStr> { + self.inner.next_back().map(Component::as_os_str) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> Iterator for Components<'a> { + type Item = Component<'a>; + + fn next(&mut self) -> Option> { + while !self.finished() { + match self.front { + // most likely case first + State::Body if !self.path.is_empty() => { + let (size, comp) = self.parse_next_component(); + self.path = &self.path[size..]; + if comp.is_some() { + return comp; + } + } + State::Body => { + self.front = State::Done; + } + State::StartDir => { + self.front = State::Body; + if self.has_physical_root { + debug_assert!(!self.path.is_empty()); + self.path = &self.path[1..]; + return Some(Component::RootDir); + } else if HAS_PREFIXES && let Some(p) = self.prefix { + if p.has_implicit_root() && !p.is_verbatim() { + return Some(Component::RootDir); + } + } else if self.include_cur_dir() { + debug_assert!(!self.path.is_empty()); + self.path = &self.path[1..]; + return Some(Component::CurDir); + } + } + _ if const { !HAS_PREFIXES } => unreachable!(), + State::Prefix if self.prefix_len() == 0 => { + self.front = State::StartDir; + } + State::Prefix => { + self.front = State::StartDir; + debug_assert!(self.prefix_len() <= self.path.len()); + let raw = &self.path[..self.prefix_len()]; + self.path = &self.path[self.prefix_len()..]; + return Some(Component::Prefix(PrefixComponent { + raw: unsafe { OsStr::from_encoded_bytes_unchecked(raw) }, + parsed: self.prefix.unwrap(), + })); + } + State::Done => unreachable!(), + } + } + None + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> DoubleEndedIterator for Components<'a> { + fn next_back(&mut self) -> Option> { + while !self.finished() { + match self.back { + State::Body if self.path.len() > self.len_before_body() => { + let (size, comp) = self.parse_next_component_back(); + self.path = &self.path[..self.path.len() - size]; + if comp.is_some() { + return comp; + } + } + State::Body => { + self.back = State::StartDir; + } + State::StartDir => { + self.back = if HAS_PREFIXES { State::Prefix } else { State::Done }; + if self.has_physical_root { + self.path = &self.path[..self.path.len() - 1]; + return Some(Component::RootDir); + } else if HAS_PREFIXES && let Some(p) = self.prefix { + if p.has_implicit_root() && !p.is_verbatim() { + return Some(Component::RootDir); + } + } else if self.include_cur_dir() { + self.path = &self.path[..self.path.len() - 1]; + return Some(Component::CurDir); + } + } + _ if !HAS_PREFIXES => unreachable!(), + State::Prefix if self.prefix_len() > 0 => { + self.back = State::Done; + return Some(Component::Prefix(PrefixComponent { + raw: unsafe { OsStr::from_encoded_bytes_unchecked(self.path) }, + parsed: self.prefix.unwrap(), + })); + } + State::Prefix => { + self.back = State::Done; + return None; + } + State::Done => unreachable!(), + } + } + None + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Components<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> PartialEq for Components<'a> { + #[inline] + fn eq(&self, other: &Components<'a>) -> bool { + let Components { path: _, front: _, back: _, has_physical_root: _, prefix: _ } = self; + + // Fast path for exact matches, e.g. for hashmap lookups. + // Don't explicitly compare the prefix or has_physical_root fields since they'll + // either be covered by the `path` buffer or are only relevant for `prefix_verbatim()`. + if self.path.len() == other.path.len() + && self.front == other.front + && self.back == State::Body + && other.back == State::Body + && self.prefix_verbatim() == other.prefix_verbatim() + { + // possible future improvement: this could bail out earlier if there were a + // reverse memcmp/bcmp comparing back to front + if self.path == other.path { + return true; + } + } + + // compare back to front since absolute paths often share long prefixes + Iterator::eq(self.clone().rev(), other.clone().rev()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Components<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a> PartialOrd for Components<'a> { + #[inline] + fn partial_cmp(&self, other: &Components<'a>) -> Option { + Some(compare_components(self.clone(), other.clone())) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Components<'_> { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + compare_components(self.clone(), other.clone()) + } +} + +fn compare_components(mut left: Components<'_>, mut right: Components<'_>) -> cmp::Ordering { + // Fast path for long shared prefixes + // + // - compare raw bytes to find first mismatch + // - backtrack to find separator before mismatch to avoid ambiguous parsings of '.' or '..' characters + // - if found update state to only do a component-wise comparison on the remainder, + // otherwise do it on the full path + // + // The fast path isn't taken for paths with a PrefixComponent to avoid backtracking into + // the middle of one + if left.prefix.is_none() && right.prefix.is_none() && left.front == right.front { + // possible future improvement: a [u8]::first_mismatch simd implementation + let first_difference = match left.path.iter().zip(right.path).position(|(&a, &b)| a != b) { + None if left.path.len() == right.path.len() => return cmp::Ordering::Equal, + None => left.path.len().min(right.path.len()), + Some(diff) => diff, + }; + + if let Some(previous_sep) = + left.path[..first_difference].iter().rposition(|&b| left.is_sep_byte(b)) + { + let mismatched_component_start = previous_sep + 1; + left.path = &left.path[mismatched_component_start..]; + left.front = State::Body; + right.path = &right.path[mismatched_component_start..]; + right.front = State::Body; + } + } + + Iterator::cmp(left, right) +} + +/// An iterator over [`Path`] and its ancestors. +/// +/// This `struct` is created by the [`ancestors`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// +/// let path = Path::new("/foo/bar"); +/// +/// for ancestor in path.ancestors() { +/// println!("{}", ancestor.display()); +/// } +/// ``` +/// +/// [`ancestors`]: Path::ancestors +#[derive(Copy, Clone, Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "path_ancestors", since = "1.28.0")] +pub struct Ancestors<'a> { + next: Option<&'a Path>, +} + +#[stable(feature = "path_ancestors", since = "1.28.0")] +impl<'a> Iterator for Ancestors<'a> { + type Item = &'a Path; + + #[inline] + fn next(&mut self) -> Option { + let next = self.next; + self.next = next.and_then(Path::parent); + next + } +} + +#[stable(feature = "path_ancestors", since = "1.28.0")] +impl FusedIterator for Ancestors<'_> {} + +//////////////////////////////////////////////////////////////////////////////// +// Basic types and traits +//////////////////////////////////////////////////////////////////////////////// + +/// An owned, mutable path (akin to [`String`]). +/// +/// This type provides methods like [`push`] and [`set_extension`] that mutate +/// the path in place. It also implements [`Deref`] to [`Path`], meaning that +/// all methods on [`Path`] slices are available on `PathBuf` values as well. +/// +/// [`push`]: PathBuf::push +/// [`set_extension`]: PathBuf::set_extension +/// +/// More details about the overall approach can be found in +/// the [module documentation](self). +/// +/// # Examples +/// +/// You can use [`push`] to build up a `PathBuf` from +/// components: +/// +/// ``` +/// use std::path::PathBuf; +/// +/// let mut path = PathBuf::new(); +/// +/// path.push(r"C:\"); +/// path.push("windows"); +/// path.push("system32"); +/// +/// path.set_extension("dll"); +/// ``` +/// +/// However, [`push`] is best used for dynamic situations. This is a better way +/// to do this when you know all of the components ahead of time: +/// +/// ``` +/// use std::path::PathBuf; +/// +/// let path: PathBuf = [r"C:\", "windows", "system32.dll"].iter().collect(); +/// ``` +/// +/// We can still do better than this! Since these are all strings, we can use +/// `From::from`: +/// +/// ``` +/// use std::path::PathBuf; +/// +/// let path = PathBuf::from(r"C:\windows\system32.dll"); +/// ``` +/// +/// Which method works best depends on what kind of situation you're in. +/// +/// Note that `PathBuf` does not always sanitize arguments, for example +/// [`push`] allows paths built from strings which include separators: +/// +/// ``` +/// use std::path::PathBuf; +/// +/// let mut path = PathBuf::new(); +/// +/// path.push(r"C:\"); +/// path.push("windows"); +/// path.push(r"..\otherdir"); +/// path.push("system32"); +/// ``` +/// +/// The behavior of `PathBuf` may be changed to a panic on such inputs +/// in the future. [`Extend::extend`] should be used to add multi-part paths. +#[cfg_attr(not(test), rustc_diagnostic_item = "PathBuf")] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct PathBuf { + inner: OsString, +} + +impl PathBuf { + /// Allocates an empty `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let path = PathBuf::new(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "1.91.0")] + pub const fn new() -> PathBuf { + PathBuf { inner: OsString::new() } + } + + /// Creates a new `PathBuf` with a given capacity used to create the + /// internal [`OsString`]. See [`with_capacity`] defined on [`OsString`]. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::with_capacity(10); + /// let capacity = path.capacity(); + /// + /// // This push is done without reallocating + /// path.push(r"C:\"); + /// + /// assert_eq!(capacity, path.capacity()); + /// ``` + /// + /// [`with_capacity`]: OsString::with_capacity + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[must_use] + #[inline] + pub fn with_capacity(capacity: usize) -> PathBuf { + PathBuf { inner: OsString::with_capacity(capacity) } + } + + /// Coerces to a [`Path`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let p = PathBuf::from("/test"); + /// assert_eq!(Path::new("/test"), p.as_path()); + /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "pathbuf_as_path")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn as_path(&self) -> &Path { + self + } + + /// Consumes and leaks the `PathBuf`, returning a mutable reference to the contents, + /// `&'a mut Path`. + /// + /// The caller has free choice over the returned lifetime, including 'static. + /// Indeed, this function is ideally used for data that lives for the remainder of + /// the program's life, as dropping the returned reference will cause a memory leak. + /// + /// It does not reallocate or shrink the `PathBuf`, so the leaked allocation may include + /// unused capacity that is not part of the returned slice. If you want to discard excess + /// capacity, call [`into_boxed_path`], and then [`Box::leak`] instead. + /// However, keep in mind that trimming the capacity may result in a reallocation and copy. + /// + /// [`into_boxed_path`]: Self::into_boxed_path + #[stable(feature = "os_string_pathbuf_leak", since = "1.89.0")] + #[inline] + pub fn leak<'a>(self) -> &'a mut Path { + Path::from_inner_mut(self.inner.leak()) + } + + /// Extends `self` with `path`. + /// + /// If `path` is absolute, it replaces the current path. + /// + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g., `\windows`), it + /// replaces everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, it replaces `self`. + /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`) + /// and `path` is not empty, the new path is normalized: all references + /// to `.` and `..` are removed. + /// + /// Consider using [`Path::join`] if you need a new `PathBuf` instead of + /// using this function on a cloned `PathBuf`. + /// + /// # Examples + /// + /// Pushing a relative path extends the existing path: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("file.bk"); + /// assert_eq!(path, PathBuf::from("/tmp/file.bk")); + /// ``` + /// + /// Pushing an absolute path replaces the existing path: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("/etc"); + /// assert_eq!(path, PathBuf::from("/etc")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("append", "put")] + pub fn push>(&mut self, path: P) { + self._push(path.as_ref()) + } + + fn _push(&mut self, path: &Path) { + // in general, a separator is needed if the rightmost byte is not a separator + let buf = self.inner.as_encoded_bytes(); + let mut need_sep = buf.last().map(|c| !is_sep_byte(*c)).unwrap_or(false); + + // in the special case of `C:` on Windows, do *not* add a separator + let comps = self.components(); + + if comps.prefix_len() > 0 + && comps.prefix_len() == comps.path.len() + && comps.prefix.unwrap().is_drive() + { + need_sep = false + } + + let need_clear = if cfg!(target_os = "cygwin") { + // If path is absolute and its prefix is none, it is like `/foo`, + // and will be handled below. + path.prefix().is_some() + } else { + // On Unix: prefix is always None. + path.is_absolute() || path.prefix().is_some() + }; + + // absolute `path` replaces `self` + if need_clear { + self.inner.truncate(0); + + // verbatim paths need . and .. removed + } else if comps.prefix_verbatim() && !path.inner.is_empty() { + let mut buf: Vec<_> = comps.collect(); + for c in path.components() { + match c { + Component::RootDir => { + buf.truncate(1); + buf.push(c); + } + Component::CurDir => (), + Component::ParentDir => { + if let Some(Component::Normal(_)) = buf.last() { + buf.pop(); + } + } + _ => buf.push(c), + } + } + + let mut res = OsString::new(); + let mut need_sep = false; + + for c in buf { + if need_sep && c != Component::RootDir { + res.push(MAIN_SEP_STR); + } + res.push(c.as_os_str()); + + need_sep = match c { + Component::RootDir => false, + Component::Prefix(prefix) => { + !prefix.parsed.is_drive() && prefix.parsed.len() > 0 + } + _ => true, + } + } + + self.inner = res; + return; + + // `path` has a root but no prefix, e.g., `\windows` (Windows only) + } else if path.has_root() { + let prefix_len = self.components().prefix_remaining(); + self.inner.truncate(prefix_len); + + // `path` is a pure relative path + } else if need_sep { + self.inner.push(MAIN_SEP_STR); + } + + self.inner.push(path); + } + + /// Truncates `self` to [`self.parent`]. + /// + /// Returns `false` and does nothing if [`self.parent`] is [`None`]. + /// Otherwise, returns `true`. + /// + /// [`self.parent`]: Path::parent + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/spirited/away.rs"); + /// + /// p.pop(); + /// assert_eq!(Path::new("/spirited"), p); + /// p.pop(); + /// assert_eq!(Path::new("/"), p); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop(&mut self) -> bool { + match self.parent().map(|p| p.as_u8_slice().len()) { + Some(len) => { + self.inner.truncate(len); + true + } + None => false, + } + } + + /// Sets whether the path has a trailing [separator](MAIN_SEPARATOR). + /// + /// The value returned by [`has_trailing_sep`](Path::has_trailing_sep) will be equivalent to + /// the provided value if possible. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir"); + /// + /// assert!(!p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(!p.has_trailing_sep()); + /// p.set_trailing_sep(true); + /// assert!(p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(!p.has_trailing_sep()); + /// + /// p = PathBuf::from("/"); + /// assert!(p.has_trailing_sep()); + /// p.set_trailing_sep(false); + /// assert!(p.has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn set_trailing_sep(&mut self, trailing_sep: bool) { + if trailing_sep { self.push_trailing_sep() } else { self.pop_trailing_sep() } + } + + /// Adds a trailing [separator](MAIN_SEPARATOR) to the path. + /// + /// This acts similarly to [`Path::with_trailing_sep`], but mutates the underlying `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir"); + /// + /// assert!(!p.has_trailing_sep()); + /// p.push_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// p.push_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// + /// p = PathBuf::from("dir/"); + /// p.push_trailing_sep(); + /// assert_eq!(p.as_os_str(), OsStr::new("dir/")); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn push_trailing_sep(&mut self) { + if !self.has_trailing_sep() { + self.push(""); + } + } + + /// Removes a trailing [separator](MAIN_SEPARATOR) from the path, if possible. + /// + /// This acts similarly to [`Path::trim_trailing_sep`], but mutates the underlying `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::PathBuf; + /// + /// let mut p = PathBuf::from("dir//"); + /// + /// assert!(p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir//")); + /// p.pop_trailing_sep(); + /// assert!(!p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir")); + /// p.pop_trailing_sep(); + /// assert!(!p.has_trailing_sep()); + /// assert_eq!(p.as_os_str(), OsStr::new("dir")); + /// + /// p = PathBuf::from("/"); + /// assert!(p.has_trailing_sep()); + /// p.pop_trailing_sep(); + /// assert!(p.has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + pub fn pop_trailing_sep(&mut self) { + self.inner.truncate(self.trim_trailing_sep().as_os_str().len()); + } + + /// Updates [`self.file_name`] to `file_name`. + /// + /// If [`self.file_name`] was [`None`], this is equivalent to pushing + /// `file_name`. + /// + /// Otherwise it is equivalent to calling [`pop`] and then pushing + /// `file_name`. The new path will be a sibling of the original path. + /// (That is, it will have the same parent.) + /// + /// The argument is not sanitized, so can include separators. This + /// behavior may be changed to a panic in the future. + /// + /// [`self.file_name`]: Path::file_name + /// [`pop`]: PathBuf::pop + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut buf = PathBuf::from("/"); + /// assert!(buf.file_name() == None); + /// + /// buf.set_file_name("foo.txt"); + /// assert!(buf == PathBuf::from("/foo.txt")); + /// assert!(buf.file_name().is_some()); + /// + /// buf.set_file_name("bar.txt"); + /// assert!(buf == PathBuf::from("/bar.txt")); + /// + /// buf.set_file_name("baz"); + /// assert!(buf == PathBuf::from("/baz")); + /// + /// buf.set_file_name("../b/c.txt"); + /// assert!(buf == PathBuf::from("/../b/c.txt")); + /// + /// buf.set_file_name("baz"); + /// assert!(buf == PathBuf::from("/../b/baz")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_file_name>(&mut self, file_name: S) { + self._set_file_name(file_name.as_ref()) + } + + fn _set_file_name(&mut self, file_name: &OsStr) { + if self.file_name().is_some() { + let popped = self.pop(); + debug_assert!(popped); + } + self.push(file_name); + } + + /// Updates [`self.extension`] to `Some(extension)` or to `None` if + /// `extension` is empty. + /// + /// Returns `false` and does nothing if [`self.file_name`] is [`None`], + /// returns `true` and updates the extension otherwise. + /// + /// If [`self.extension`] is [`None`], the extension is added; otherwise + /// it is replaced. + /// + /// If `extension` is the empty string, [`self.extension`] will be [`None`] + /// afterwards, not `Some("")`. + /// + /// # Panics + /// + /// Panics if the passed extension contains a path separator (see + /// [`is_separator`]). + /// + /// # Caveats + /// + /// The new `extension` may contain dots and will be used in its entirety, + /// but only the part after the final dot will be reflected in + /// [`self.extension`]. + /// + /// If the file stem contains internal dots and `extension` is empty, part + /// of the old file stem will be considered the new [`self.extension`]. + /// + /// See the examples below. + /// + /// [`self.file_name`]: Path::file_name + /// [`self.extension`]: Path::extension + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/feel/the"); + /// + /// p.set_extension("force"); + /// assert_eq!(Path::new("/feel/the.force"), p.as_path()); + /// + /// p.set_extension("dark.side"); + /// assert_eq!(Path::new("/feel/the.dark.side"), p.as_path()); + /// + /// p.set_extension("cookie"); + /// assert_eq!(Path::new("/feel/the.dark.cookie"), p.as_path()); + /// + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the.dark"), p.as_path()); + /// + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the"), p.as_path()); + /// + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the"), p.as_path()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn set_extension>(&mut self, extension: S) -> bool { + self._set_extension(extension.as_ref()) + } + + fn _set_extension(&mut self, extension: &OsStr) -> bool { + validate_extension(extension); + + let file_stem = match self.file_stem() { + None => return false, + Some(f) => f.as_encoded_bytes(), + }; + + // truncate until right after the file stem + let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr(); + let start = self.inner.as_encoded_bytes().as_ptr().addr(); + self.inner.truncate(end_file_stem.wrapping_sub(start)); + + // add the new extension, if any + let new = extension.as_encoded_bytes(); + if !new.is_empty() { + self.inner.reserve_exact(new.len() + 1); + self.inner.push("."); + // SAFETY: Since a UTF-8 string was just pushed, it is not possible + // for the buffer to end with a surrogate half. + unsafe { self.inner.extend_from_slice_unchecked(new) }; + } + + true + } + + /// Append [`self.extension`] with `extension`. + /// + /// Returns `false` and does nothing if [`self.file_name`] is [`None`], + /// returns `true` and updates the extension otherwise. + /// + /// # Panics + /// + /// Panics if the passed extension contains a path separator (see + /// [`is_separator`]). + /// + /// # Caveats + /// + /// The appended `extension` may contain dots and will be used in its entirety, + /// but only the part after the final dot will be reflected in + /// [`self.extension`]. + /// + /// See the examples below. + /// + /// [`self.file_name`]: Path::file_name + /// [`self.extension`]: Path::extension + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/feel/the"); + /// + /// p.add_extension("formatted"); + /// assert_eq!(Path::new("/feel/the.formatted"), p.as_path()); + /// + /// p.add_extension("dark.side"); + /// assert_eq!(Path::new("/feel/the.formatted.dark.side"), p.as_path()); + /// + /// p.set_extension("cookie"); + /// assert_eq!(Path::new("/feel/the.formatted.dark.cookie"), p.as_path()); + /// + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); + /// + /// p.add_extension(""); + /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); + /// ``` + #[stable(feature = "path_add_extension", since = "1.91.0")] + pub fn add_extension>(&mut self, extension: S) -> bool { + self._add_extension(extension.as_ref()) + } + + fn _add_extension(&mut self, extension: &OsStr) -> bool { + validate_extension(extension); + + let file_name = match self.file_name() { + None => return false, + Some(f) => f.as_encoded_bytes(), + }; + + let new = extension.as_encoded_bytes(); + if !new.is_empty() { + // truncate until right after the file name + // this is necessary for trimming the trailing separator + let end_file_name = file_name[file_name.len()..].as_ptr().addr(); + let start = self.inner.as_encoded_bytes().as_ptr().addr(); + self.inner.truncate(end_file_name.wrapping_sub(start)); + + // append the new extension + self.inner.reserve_exact(new.len() + 1); + self.inner.push("."); + // SAFETY: Since a UTF-8 string was just pushed, it is not possible + // for the buffer to end with a surrogate half. + unsafe { self.inner.extend_from_slice_unchecked(new) }; + } + + true + } + + /// Yields a mutable reference to the underlying [`OsString`] instance. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut path = PathBuf::from("/foo"); + /// + /// path.push("bar"); + /// assert_eq!(path, Path::new("/foo/bar")); + /// + /// // OsString's `push` does not add a separator. + /// path.as_mut_os_string().push("baz"); + /// assert_eq!(path, Path::new("/foo/barbaz")); + /// ``` + #[stable(feature = "path_as_mut_os_str", since = "1.70.0")] + #[must_use] + #[inline] + pub fn as_mut_os_string(&mut self) -> &mut OsString { + &mut self.inner + } + + /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. + /// + /// # Examples + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let p = PathBuf::from("/the/head"); + /// let os_str = p.into_os_string(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + pub fn into_os_string(self) -> OsString { + self.inner + } + + /// Converts this `PathBuf` into a [boxed](Box) [`Path`]. + #[stable(feature = "into_boxed_path", since = "1.20.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + pub fn into_boxed_path(self) -> Box { + let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path; + unsafe { Box::from_raw(rw) } + } + + /// Invokes [`capacity`] on the underlying instance of [`OsString`]. + /// + /// [`capacity`]: OsString::capacity + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[must_use] + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + /// Invokes [`clear`] on the underlying instance of [`OsString`]. + /// + /// [`clear`]: OsString::clear + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + /// Invokes [`reserve`] on the underlying instance of [`OsString`]. + /// + /// [`reserve`]: OsString::reserve + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + /// Invokes [`try_reserve`] on the underlying instance of [`OsString`]. + /// + /// [`try_reserve`]: OsString::try_reserve + #[stable(feature = "try_reserve_2", since = "1.63.0")] + #[inline] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve(additional) + } + + /// Invokes [`reserve_exact`] on the underlying instance of [`OsString`]. + /// + /// [`reserve_exact`]: OsString::reserve_exact + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + /// Invokes [`try_reserve_exact`] on the underlying instance of [`OsString`]. + /// + /// [`try_reserve_exact`]: OsString::try_reserve_exact + #[stable(feature = "try_reserve_2", since = "1.63.0")] + #[inline] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve_exact(additional) + } + + /// Invokes [`shrink_to_fit`] on the underlying instance of [`OsString`]. + /// + /// [`shrink_to_fit`]: OsString::shrink_to_fit + #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + /// Invokes [`shrink_to`] on the underlying instance of [`OsString`]. + /// + /// [`shrink_to`]: OsString::shrink_to + #[stable(feature = "shrink_to", since = "1.56.0")] + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for PathBuf { + #[inline] + fn clone(&self) -> Self { + PathBuf { inner: self.inner.clone() } + } + + /// Clones the contents of `source` into `self`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. + #[inline] + fn clone_from(&mut self, source: &Self) { + self.inner.clone_from(&source.inner) + } +} + +#[stable(feature = "box_from_path", since = "1.17.0")] +impl From<&Path> for Box { + /// Creates a boxed [`Path`] from a reference. + /// + /// This will allocate and clone `path` to it. + fn from(path: &Path) -> Box { + Box::clone_from_ref(path) + } +} + +#[stable(feature = "box_from_mut_slice", since = "1.84.0")] +impl From<&mut Path> for Box { + /// Creates a boxed [`Path`] from a reference. + /// + /// This will allocate and clone `path` to it. + fn from(path: &mut Path) -> Box { + Self::from(&*path) + } +} + +#[stable(feature = "box_from_cow", since = "1.45.0")] +impl From> for Box { + /// Creates a boxed [`Path`] from a clone-on-write pointer. + /// + /// Converting from a `Cow::Owned` does not clone or allocate. + #[inline] + fn from(cow: Cow<'_, Path>) -> Box { + match cow { + Cow::Borrowed(path) => Box::from(path), + Cow::Owned(path) => Box::from(path), + } + } +} + +#[stable(feature = "path_buf_from_box", since = "1.18.0")] +impl From> for PathBuf { + /// Converts a [Box]<[Path]> into a [`PathBuf`]. + /// + /// This conversion does not allocate or copy memory. + #[inline] + fn from(boxed: Box) -> PathBuf { + boxed.into_path_buf() + } +} + +#[stable(feature = "box_from_path_buf", since = "1.20.0")] +impl From for Box { + /// Converts a [`PathBuf`] into a [Box]<[Path]>. + /// + /// This conversion currently should not allocate memory, + /// but this behavior is not guaranteed on all platforms or in all future versions. + #[inline] + fn from(p: PathBuf) -> Box { + p.into_boxed_path() + } +} + +#[stable(feature = "more_box_slice_clone", since = "1.29.0")] +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + self.to_path_buf().into_boxed_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl> From<&T> for PathBuf { + /// Converts a borrowed [`OsStr`] to a [`PathBuf`]. + /// + /// Allocates a [`PathBuf`] and copies the data into it. + #[inline] + fn from(s: &T) -> PathBuf { + PathBuf::from(s.as_ref().to_os_string()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From for PathBuf { + /// Converts an [`OsString`] into a [`PathBuf`]. + /// + /// This conversion does not allocate or copy memory. + #[inline] + fn from(s: OsString) -> PathBuf { + PathBuf { inner: s } + } +} + +#[stable(feature = "from_path_buf_for_os_string", since = "1.14.0")] +impl From for OsString { + /// Converts a [`PathBuf`] into an [`OsString`] + /// + /// This conversion does not allocate or copy memory. + #[inline] + fn from(path_buf: PathBuf) -> OsString { + path_buf.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From for PathBuf { + /// Converts a [`String`] into a [`PathBuf`] + /// + /// This conversion does not allocate or copy memory. + #[inline] + fn from(s: String) -> PathBuf { + PathBuf::from(OsString::from(s)) + } +} + +#[stable(feature = "path_from_str", since = "1.32.0")] +impl FromStr for PathBuf { + type Err = core::convert::Infallible; + + #[inline] + fn from_str(s: &str) -> Result { + Ok(PathBuf::from(s)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl> FromIterator

for PathBuf { + /// Creates a new `PathBuf` from the [`Path`] elements of an iterator. + /// + /// This uses [`push`](Self::push) to add each element, so can be used to adjoin multiple path + /// [components](Components). + /// + /// # Examples + /// ``` + /// # use std::path::PathBuf; + /// let path = PathBuf::from_iter(["/tmp", "foo", "bar"]); + /// assert_eq!(path, PathBuf::from("/tmp/foo/bar")); + /// ``` + /// + /// See documentation for [`push`](Self::push) for more details on how the path is constructed. + fn from_iter>(iter: I) -> PathBuf { + let mut buf = PathBuf::new(); + buf.extend(iter); + buf + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl> Extend

for PathBuf { + /// Extends `self` with [`Path`] elements from `iter`. + /// + /// This uses [`push`](Self::push) to add each element, so can be used to adjoin multiple path + /// [components](Components). + /// + /// # Examples + /// ``` + /// # use std::path::PathBuf; + /// let mut path = PathBuf::from("/tmp"); + /// path.extend(["foo", "bar", "file.txt"]); + /// assert_eq!(path, PathBuf::from("/tmp/foo/bar/file.txt")); + /// ``` + /// + /// See documentation for [`push`](Self::push) for more details on how the path is constructed. + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |p| self.push(p.as_ref())); + } + + #[inline] + fn extend_one(&mut self, p: P) { + self.push(p.as_ref()); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for PathBuf { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, formatter) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Deref for PathBuf { + type Target = Path; + #[inline] + fn deref(&self) -> &Path { + Path::new(&self.inner) + } +} + +#[stable(feature = "path_buf_deref_mut", since = "1.68.0")] +impl ops::DerefMut for PathBuf { + #[inline] + fn deref_mut(&mut self) -> &mut Path { + Path::from_inner_mut(&mut self.inner) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow for PathBuf { + #[inline] + fn borrow(&self) -> &Path { + self.deref() + } +} + +#[stable(feature = "default_for_pathbuf", since = "1.17.0")] +impl Default for PathBuf { + #[inline] + fn default() -> Self { + PathBuf::new() + } +} + +#[stable(feature = "cow_from_path", since = "1.6.0")] +impl<'a> From<&'a Path> for Cow<'a, Path> { + /// Creates a clone-on-write pointer from a reference to + /// [`Path`]. + /// + /// This conversion does not clone or allocate. + #[inline] + fn from(s: &'a Path) -> Cow<'a, Path> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_path", since = "1.6.0")] +impl<'a> From for Cow<'a, Path> { + /// Creates a clone-on-write pointer from an owned + /// instance of [`PathBuf`]. + /// + /// This conversion does not clone or allocate. + #[inline] + fn from(s: PathBuf) -> Cow<'a, Path> { + Cow::Owned(s) + } +} + +#[stable(feature = "cow_from_pathbuf_ref", since = "1.28.0")] +impl<'a> From<&'a PathBuf> for Cow<'a, Path> { + /// Creates a clone-on-write pointer from a reference to + /// [`PathBuf`]. + /// + /// This conversion does not clone or allocate. + #[inline] + fn from(p: &'a PathBuf) -> Cow<'a, Path> { + Cow::Borrowed(p.as_path()) + } +} + +#[stable(feature = "pathbuf_from_cow_path", since = "1.28.0")] +impl<'a> From> for PathBuf { + /// Converts a clone-on-write pointer to an owned path. + /// + /// Converting from a `Cow::Owned` does not clone or allocate. + #[inline] + fn from(p: Cow<'a, Path>) -> Self { + p.into_owned() + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From for Arc { + /// Converts a [`PathBuf`] into an [Arc]<[Path]> by moving the [`PathBuf`] data + /// into a new [`Arc`] buffer. + #[inline] + fn from(s: PathBuf) -> Arc { + let arc: Arc = Arc::from(s.into_os_string()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From<&Path> for Arc { + /// Converts a [`Path`] into an [`Arc`] by copying the [`Path`] data into a new [`Arc`] buffer. + #[inline] + fn from(s: &Path) -> Arc { + let arc: Arc = Arc::from(s.as_os_str()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +impl From<&mut Path> for Arc { + /// Converts a [`Path`] into an [`Arc`] by copying the [`Path`] data into a new [`Arc`] buffer. + #[inline] + fn from(s: &mut Path) -> Arc { + Arc::from(&*s) + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From for Rc { + /// Converts a [`PathBuf`] into an [Rc]<[Path]> by moving the [`PathBuf`] data into + /// a new [`Rc`] buffer. + #[inline] + fn from(s: PathBuf) -> Rc { + let rc: Rc = Rc::from(s.into_os_string()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +#[stable(feature = "shared_from_slice2", since = "1.24.0")] +impl From<&Path> for Rc { + /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new [`Rc`] buffer. + #[inline] + fn from(s: &Path) -> Rc { + let rc: Rc = Rc::from(s.as_os_str()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +impl From<&mut Path> for Rc { + /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new [`Rc`] buffer. + #[inline] + fn from(s: &mut Path) -> Rc { + Rc::from(&*s) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ToOwned for Path { + type Owned = PathBuf; + #[inline] + fn to_owned(&self) -> PathBuf { + self.to_path_buf() + } + #[inline] + fn clone_into(&self, target: &mut PathBuf) { + self.inner.clone_into(&mut target.inner); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for PathBuf { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + self.components() == other.components() + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for PathBuf { + #[inline] + fn eq(&self, other: &str) -> bool { + self.as_path() == other + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for str { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + self == other.as_path() + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for PathBuf { + #[inline] + fn eq(&self, other: &String) -> bool { + self.as_path() == other.as_str() + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for String { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + self.as_str() == other.as_path() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for PathBuf { + fn hash(&self, h: &mut H) { + self.as_path().hash(h) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for PathBuf {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for PathBuf { + #[inline] + fn partial_cmp(&self, other: &PathBuf) -> Option { + Some(compare_components(self.components(), other.components())) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for PathBuf { + #[inline] + fn cmp(&self, other: &PathBuf) -> cmp::Ordering { + compare_components(self.components(), other.components()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for PathBuf { + #[inline] + fn as_ref(&self) -> &OsStr { + &self.inner[..] + } +} + +/// A slice of a path (akin to [`str`]). +/// +/// This type supports a number of operations for inspecting a path, including +/// breaking the path into its components (separated by `/` on Unix and by either +/// `/` or `\` on Windows), extracting the file name, determining whether the path +/// is absolute, and so on. +/// +/// This is an *unsized* type, meaning that it must always be used behind a +/// pointer like `&` or [`Box`]. For an owned version of this type, +/// see [`PathBuf`]. +/// +/// More details about the overall approach can be found in +/// the [module documentation](self). +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use std::ffi::OsStr; +/// +/// // Note: this example does work on Windows +/// let path = Path::new("./foo/bar.txt"); +/// +/// let parent = path.parent(); +/// assert_eq!(parent, Some(Path::new("./foo"))); +/// +/// let file_stem = path.file_stem(); +/// assert_eq!(file_stem, Some(OsStr::new("bar"))); +/// +/// let extension = path.extension(); +/// assert_eq!(extension, Some(OsStr::new("txt"))); +/// ``` +#[cfg_attr(not(test), rustc_diagnostic_item = "Path")] +#[stable(feature = "rust1", since = "1.0.0")] +// `Path::new` and `impl CloneToUninit for Path` current implementation relies +// on `Path` being layout-compatible with `OsStr`. +// However, `Path` layout is considered an implementation detail and must not be relied upon. +#[repr(transparent)] +pub struct Path { + inner: OsStr, +} + +/// An error returned from [`Path::strip_prefix`] if the prefix was not found. +/// +/// This `struct` is created by the [`strip_prefix`] method on [`Path`]. +/// See its documentation for more. +/// +/// [`strip_prefix`]: Path::strip_prefix +#[derive(Debug, Clone, PartialEq, Eq)] +#[stable(since = "1.7.0", feature = "strip_prefix")] +pub struct StripPrefixError(()); + +/// An error returned from [`Path::normalize_lexically`] if a `..` parent reference +/// would escape the path. +#[unstable(feature = "normalize_lexically", issue = "134694")] +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct NormalizeError; + +impl Path { + // The following (private!) function allows construction of a path from a u8 + // slice, which is only safe when it is known to follow the OsStr encoding. + unsafe fn from_u8_slice(s: &[u8]) -> &Path { + unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(s)) } + } + // The following (private!) function reveals the byte encoding used for OsStr. + pub(crate) fn as_u8_slice(&self) -> &[u8] { + self.inner.as_encoded_bytes() + } + + /// Directly wraps a string slice as a `Path` slice. + /// + /// This is a cost-free conversion. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// Path::new("foo.txt"); + /// ``` + /// + /// You can create `Path`s from `String`s, or even other `Path`s: + /// + /// ``` + /// use std::path::Path; + /// + /// let string = String::from("foo.txt"); + /// let from_string = Path::new(&string); + /// let from_path = Path::new(&from_string); + /// assert_eq!(from_string, from_path); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + pub const fn new + ?Sized>(s: &S) -> &Path { + unsafe { &*(s.as_ref() as *const OsStr as *const Path) } + } + + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + const fn from_inner_mut(inner: &mut OsStr) -> &mut Path { + // SAFETY: Path is just a wrapper around OsStr, + // therefore converting &mut OsStr to &mut Path is safe. + unsafe { &mut *(inner as *mut OsStr as *mut Path) } + } + + /// Yields the underlying [`OsStr`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let os_str = Path::new("foo.txt").as_os_str(); + /// assert_eq!(os_str, std::ffi::OsStr::new("foo.txt")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn as_os_str(&self) -> &OsStr { + &self.inner + } + + /// Yields a mutable reference to the underlying [`OsStr`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut path = PathBuf::from("Foo.TXT"); + /// + /// assert_ne!(path, Path::new("foo.txt")); + /// + /// path.as_mut_os_str().make_ascii_lowercase(); + /// assert_eq!(path, Path::new("foo.txt")); + /// ``` + #[stable(feature = "path_as_mut_os_str", since = "1.70.0")] + #[must_use] + #[inline] + pub fn as_mut_os_str(&mut self) -> &mut OsStr { + &mut self.inner + } + + /// Yields a [`&str`] slice if the `Path` is valid unicode. + /// + /// This conversion may entail doing a check for UTF-8 validity. + /// Note that validation is performed because non-UTF-8 strings are + /// perfectly valid for some OS. + /// + /// [`&str`]: str + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_str(), Some("foo.txt")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() + } + + /// Converts a `Path` to a [`Cow`]. + /// + /// Any non-UTF-8 sequences are replaced with + /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. + /// + /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER + /// + /// # Examples + /// + /// Calling `to_string_lossy` on a `Path` with valid unicode: + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo.txt"); + /// assert_eq!(path.to_string_lossy(), "foo.txt"); + /// ``` + /// + /// Had `path` contained invalid unicode, the `to_string_lossy` call might + /// have returned `"fo�.txt"`. + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub fn to_string_lossy(&self) -> Cow<'_, str> { + self.inner.to_string_lossy() + } + + /// Converts a `Path` to an owned [`PathBuf`]. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let path_buf = Path::new("foo.txt").to_path_buf(); + /// assert_eq!(path_buf, PathBuf::from("foo.txt")); + /// ``` + #[rustc_conversion_suggestion] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "path_to_pathbuf")] + pub fn to_path_buf(&self) -> PathBuf { + PathBuf::from(self.inner.to_os_string()) + } + + /// Returns `true` if the `Path` is absolute, i.e., if it is independent of + /// the current directory. + /// + /// * On Unix, a path is absolute if it starts with the root, so + /// `is_absolute` and [`has_root`] are equivalent. + /// + /// * On Windows, a path is absolute if it has a prefix and starts with the + /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert!(!Path::new("foo.txt").is_absolute()); + /// ``` + /// + /// [`has_root`]: Path::has_root + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[allow(deprecated)] + pub fn is_absolute(&self) -> bool { + sys::path::is_absolute(self) + } + + /// Returns `true` if the `Path` is relative, i.e., not absolute. + /// + /// See [`is_absolute`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert!(Path::new("foo.txt").is_relative()); + /// ``` + /// + /// [`is_absolute`]: Path::is_absolute + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn is_relative(&self) -> bool { + !self.is_absolute() + } + + pub(crate) fn prefix(&self) -> Option> { + self.components().prefix + } + + /// Returns `true` if the `Path` has a root. + /// + /// * On Unix, a path has a root if it begins with `/`. + /// + /// * On Windows, a path has a root if it: + /// * has no prefix and begins with a separator, e.g., `\windows` + /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` + /// * has any non-disk prefix, e.g., `\\server\share` + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert!(Path::new("/etc/passwd").has_root()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn has_root(&self) -> bool { + self.components().has_root() + } + + /// Returns the `Path` without its final component, if there is one. + /// + /// This means it returns `Some("")` for relative paths with one component. + /// + /// Returns [`None`] if the path terminates in a root or prefix, or if it's + /// the empty string. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/foo/bar"); + /// let parent = path.parent().unwrap(); + /// assert_eq!(parent, Path::new("/foo")); + /// + /// let grand_parent = parent.parent().unwrap(); + /// assert_eq!(grand_parent, Path::new("/")); + /// assert_eq!(grand_parent.parent(), None); + /// + /// let relative_path = Path::new("foo/bar"); + /// let parent = relative_path.parent(); + /// assert_eq!(parent, Some(Path::new("foo"))); + /// let grand_parent = parent.and_then(Path::parent); + /// assert_eq!(grand_parent, Some(Path::new(""))); + /// let great_grand_parent = grand_parent.and_then(Path::parent); + /// assert_eq!(great_grand_parent, None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(alias = "dirname")] + #[must_use] + pub fn parent(&self) -> Option<&Path> { + let mut comps = self.components(); + let comp = comps.next_back(); + comp.and_then(|p| match p { + Component::Normal(_) | Component::CurDir | Component::ParentDir => { + Some(comps.as_path()) + } + _ => None, + }) + } + + /// Produces an iterator over `Path` and its ancestors. + /// + /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero + /// or more times. If the [`parent`] method returns [`None`], the iterator will do likewise. + /// The iterator will always yield at least one value, namely `Some(&self)`. Next it will yield + /// `&self.parent()`, `&self.parent().and_then(Path::parent)` and so on. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let mut ancestors = Path::new("/foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar"))); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo"))); + /// assert_eq!(ancestors.next(), Some(Path::new("/"))); + /// assert_eq!(ancestors.next(), None); + /// + /// let mut ancestors = Path::new("../foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("../foo/bar"))); + /// assert_eq!(ancestors.next(), Some(Path::new("../foo"))); + /// assert_eq!(ancestors.next(), Some(Path::new(".."))); + /// assert_eq!(ancestors.next(), Some(Path::new(""))); + /// assert_eq!(ancestors.next(), None); + /// ``` + /// + /// [`parent`]: Path::parent + #[stable(feature = "path_ancestors", since = "1.28.0")] + #[inline] + pub fn ancestors(&self) -> Ancestors<'_> { + Ancestors { next: Some(&self) } + } + + /// Returns the final component of the `Path`, if there is one. + /// + /// If the path is a normal file, this is the file name. If it's the path of a directory, this + /// is the directory name. + /// + /// Returns [`None`] if the path terminates in `..`. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// use std::ffi::OsStr; + /// + /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); + /// assert_eq!(None, Path::new("foo.txt/..").file_name()); + /// assert_eq!(None, Path::new("/").file_name()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(alias = "basename")] + #[must_use] + pub fn file_name(&self) -> Option<&OsStr> { + self.components().next_back().and_then(|p| match p { + Component::Normal(p) => Some(p), + _ => None, + }) + } + + /// Returns a path that, when joined onto `base`, yields `self`. + /// + /// # Errors + /// + /// If `base` is not a prefix of `self` (i.e., [`starts_with`] + /// returns `false`), returns [`Err`]. + /// + /// [`starts_with`]: Path::starts_with + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/test/haha/foo.txt"); + /// + /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); + /// + /// assert!(path.strip_prefix("test").is_err()); + /// assert!(path.strip_prefix("/te").is_err()); + /// assert!(path.strip_prefix("/haha").is_err()); + /// + /// let prefix = PathBuf::from("/test/"); + /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); + /// ``` + #[stable(since = "1.7.0", feature = "path_strip_prefix")] + pub fn strip_prefix

(&self, base: P) -> Result<&Path, StripPrefixError> + where + P: AsRef, + { + self._strip_prefix(base.as_ref()) + } + + fn _strip_prefix(&self, base: &Path) -> Result<&Path, StripPrefixError> { + iter_after(self.components(), base.components()) + .map(|c| c.as_path()) + .ok_or(StripPrefixError(())) + } + + /// Determines whether `base` is a prefix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.starts_with("/etc")); + /// assert!(path.starts_with("/etc/")); + /// assert!(path.starts_with("/etc/passwd")); + /// assert!(path.starts_with("/etc/passwd/")); // extra slash is okay + /// assert!(path.starts_with("/etc/passwd///")); // multiple extra slashes are okay + /// + /// assert!(!path.starts_with("/e")); + /// assert!(!path.starts_with("/etc/passwd.txt")); + /// + /// assert!(!Path::new("/etc/foo.rs").starts_with("/etc/foo")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn starts_with>(&self, base: P) -> bool { + self._starts_with(base.as_ref()) + } + + fn _starts_with(&self, base: &Path) -> bool { + iter_after(self.components(), base.components()).is_some() + } + + /// Determines whether `child` is a suffix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/etc/resolv.conf"); + /// + /// assert!(path.ends_with("resolv.conf")); + /// assert!(path.ends_with("etc/resolv.conf")); + /// assert!(path.ends_with("/etc/resolv.conf")); + /// + /// assert!(!path.ends_with("/resolv.conf")); + /// assert!(!path.ends_with("conf")); // use .extension() instead + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn ends_with>(&self, child: P) -> bool { + self._ends_with(child.as_ref()) + } + + fn _ends_with(&self, child: &Path) -> bool { + iter_after(self.components().rev(), child.components().rev()).is_some() + } + + /// Checks whether the `Path` is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_is_empty)] + /// use std::path::Path; + /// + /// let path = Path::new(""); + /// assert!(path.is_empty()); + /// + /// let path = Path::new("foo"); + /// assert!(!path.is_empty()); + /// + /// let path = Path::new("."); + /// assert!(!path.is_empty()); + /// ``` + #[unstable(feature = "path_is_empty", issue = "148494")] + pub fn is_empty(&self) -> bool { + self.as_os_str().is_empty() + } + + /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// + /// [`self.file_name`]: Path::file_name + /// + /// The stem is: + /// + /// * [`None`], if there is no file name; + /// * The entire file name if there is no embedded `.`; + /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * Otherwise, the portion of the file name before the final `.` + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap()); + /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap()); + /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_prefix`], which extracts the portion of the file name + /// before the *first* `.` + /// + /// [`Path::file_prefix`]: Path::file_prefix + /// + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn file_stem(&self) -> Option<&OsStr> { + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.or(after)) + } + + /// Extracts the prefix of [`self.file_name`]. + /// + /// The prefix is: + /// + /// * [`None`], if there is no file name; + /// * The entire file name if there is no embedded `.`; + /// * The portion of the file name before the first non-beginning `.`; + /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * The portion of the file name before the second `.` if the file name begins with `.` + /// + /// [`self.file_name`]: Path::file_name + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); + /// assert_eq!("foo", Path::new("foo.tar.gz").file_prefix().unwrap()); + /// assert_eq!(".config", Path::new(".config").file_prefix().unwrap()); + /// assert_eq!(".config", Path::new(".config.toml").file_prefix().unwrap()); + /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_stem`], which extracts the portion of the file name + /// before the *last* `.` + /// + /// [`Path::file_stem`]: Path::file_stem + /// + #[stable(feature = "path_file_prefix", since = "1.91.0")] + #[must_use] + pub fn file_prefix(&self) -> Option<&OsStr> { + self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) + } + + /// Extracts the extension (without the leading dot) of [`self.file_name`], if possible. + /// + /// The extension is: + /// + /// * [`None`], if there is no file name; + /// * [`None`], if there is no embedded `.`; + /// * [`None`], if the file name begins with `.` and has no other `.`s within; + /// * Otherwise, the portion of the file name after the final `.` + /// + /// [`self.file_name`]: Path::file_name + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// assert_eq!("rs", Path::new("foo.rs").extension().unwrap()); + /// assert_eq!("gz", Path::new("foo.tar.gz").extension().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn extension(&self) -> Option<&OsStr> { + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after)) + } + + /// Checks whether the path ends in a trailing [separator](MAIN_SEPARATOR). + /// + /// This is generally done to ensure that a path is treated as a directory, not a file, + /// although it does not actually guarantee that such a path is a directory on the underlying + /// file system. + /// + /// Despite this behavior, two paths are still considered the same in Rust whether they have a + /// trailing separator or not. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::path::Path; + /// + /// assert!(Path::new("dir/").has_trailing_sep()); + /// assert!(!Path::new("file.rs").has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn has_trailing_sep(&self) -> bool { + self.as_os_str().as_encoded_bytes().last().copied().is_some_and(is_sep_byte) + } + + /// Ensures that a path has a trailing [separator](MAIN_SEPARATOR), + /// allocating a [`PathBuf`] if necessary. + /// + /// The resulting path will return true for [`has_trailing_sep`](Self::has_trailing_sep). + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::Path; + /// + /// assert_eq!(Path::new("dir//").with_trailing_sep().as_os_str(), OsStr::new("dir//")); + /// assert_eq!(Path::new("dir/").with_trailing_sep().as_os_str(), OsStr::new("dir/")); + /// assert!(!Path::new("dir").has_trailing_sep()); + /// assert!(Path::new("dir").with_trailing_sep().has_trailing_sep()); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn with_trailing_sep(&self) -> Cow<'_, Path> { + if self.has_trailing_sep() { Cow::Borrowed(self) } else { Cow::Owned(self.join("")) } + } + + /// Trims a trailing [separator](MAIN_SEPARATOR) from a path, if possible. + /// + /// The resulting path will return false for [`has_trailing_sep`](Self::has_trailing_sep) for + /// most paths. + /// + /// Some paths, like `/`, cannot be trimmed in this way. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_trailing_sep)] + /// use std::ffi::OsStr; + /// use std::path::Path; + /// + /// assert_eq!(Path::new("dir//").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("dir/").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("dir").trim_trailing_sep().as_os_str(), OsStr::new("dir")); + /// assert_eq!(Path::new("/").trim_trailing_sep().as_os_str(), OsStr::new("/")); + /// assert_eq!(Path::new("//").trim_trailing_sep().as_os_str(), OsStr::new("//")); + /// ``` + #[unstable(feature = "path_trailing_sep", issue = "142503")] + #[must_use] + #[inline] + pub fn trim_trailing_sep(&self) -> &Path { + if self.has_trailing_sep() && (!self.has_root() || self.parent().is_some()) { + let mut bytes = self.inner.as_encoded_bytes(); + while let Some((last, init)) = bytes.split_last() + && is_sep_byte(*last) + { + bytes = init; + } + + // SAFETY: Trimming trailing ASCII bytes will retain the validity of the string. + Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }) + } else { + self + } + } + + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. + /// + /// If `path` is absolute, it replaces the current path. + /// + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g., `\windows`), it + /// replaces and returns everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, `self` is ignored and `path` is returned. + /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`) + /// and `path` is not empty, the new path is normalized: all references + /// to `.` and `..` are removed. + /// + /// See [`PathBuf::push`] for more details on what it means to adjoin a path. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn join>(&self, path: P) -> PathBuf { + self._join(path.as_ref()) + } + + fn _join(&self, path: &Path) -> PathBuf { + let mut buf = self.to_path_buf(); + buf.push(path); + buf + } + + /// Creates an owned [`PathBuf`] like `self` but with the given file name. + /// + /// See [`PathBuf::set_file_name`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/tmp/foo.png"); + /// assert_eq!(path.with_file_name("bar"), PathBuf::from("/tmp/bar")); + /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt")); + /// + /// let path = Path::new("/tmp"); + /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn with_file_name>(&self, file_name: S) -> PathBuf { + self._with_file_name(file_name.as_ref()) + } + + fn _with_file_name(&self, file_name: &OsStr) -> PathBuf { + let mut buf = self.to_path_buf(); + buf.set_file_name(file_name); + buf + } + + /// Creates an owned [`PathBuf`] like `self` but with the given extension. + /// + /// See [`PathBuf::set_extension`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo.rs"); + /// assert_eq!(path.with_extension("txt"), Path::new("foo.txt")); + /// assert_eq!(path.with_extension(""), Path::new("foo")); + /// ``` + /// + /// Handling multiple extensions: + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo.tar.gz"); + /// assert_eq!(path.with_extension("xz"), Path::new("foo.tar.xz")); + /// assert_eq!(path.with_extension("").with_extension("txt"), Path::new("foo.txt")); + /// ``` + /// + /// Adding an extension where one did not exist: + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("foo"); + /// assert_eq!(path.with_extension("rs"), Path::new("foo.rs")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_extension>(&self, extension: S) -> PathBuf { + self._with_extension(extension.as_ref()) + } + + fn _with_extension(&self, extension: &OsStr) -> PathBuf { + let self_len = self.as_os_str().len(); + let self_bytes = self.as_os_str().as_encoded_bytes(); + + let (new_capacity, slice_to_copy) = match self.extension() { + None => { + // Enough capacity for the extension and the dot + let capacity = self_len + extension.len() + 1; + let whole_path = self_bytes; + (capacity, whole_path) + } + Some(previous_extension) => { + let capacity = self_len + extension.len() - previous_extension.len(); + let path_till_dot = &self_bytes[..self_len - previous_extension.len()]; + (capacity, path_till_dot) + } + }; + + let mut new_path = PathBuf::with_capacity(new_capacity); + // SAFETY: The path is empty, so cannot have surrogate halves. + unsafe { new_path.inner.extend_from_slice_unchecked(slice_to_copy) }; + new_path.set_extension(extension); + new_path + } + + /// Creates an owned [`PathBuf`] like `self` but with the extension added. + /// + /// See [`PathBuf::add_extension`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("foo.rs"); + /// assert_eq!(path.with_added_extension("txt"), PathBuf::from("foo.rs.txt")); + /// + /// let path = Path::new("foo.tar.gz"); + /// assert_eq!(path.with_added_extension(""), PathBuf::from("foo.tar.gz")); + /// assert_eq!(path.with_added_extension("xz"), PathBuf::from("foo.tar.gz.xz")); + /// assert_eq!(path.with_added_extension("").with_added_extension("txt"), PathBuf::from("foo.tar.gz.txt")); + /// ``` + #[stable(feature = "path_add_extension", since = "1.91.0")] + pub fn with_added_extension>(&self, extension: S) -> PathBuf { + let mut new_path = self.to_path_buf(); + new_path.add_extension(extension); + new_path + } + + /// Produces an iterator over the [`Component`]s of the path. + /// + /// When parsing the path, there is a small amount of normalization: + /// + /// * Repeated separators are ignored, so `a/b` and `a//b` both have + /// `a` and `b` as components. + /// + /// * Occurrences of `.` are normalized away, except if they are at the + /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and + /// `a/b` all have `a` and `b` as components, but `./a/b` starts with + /// an additional [`CurDir`] component. + /// + /// * Trailing separators are normalized away, so `/a/b` and `/a/b/` are equivalent. + /// + /// Note that no other normalization takes place; in particular, `a/c` + /// and `a/b/../c` are distinct, to account for the possibility that `b` + /// is a symbolic link (so its parent isn't `a`). + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, Component}; + /// use std::ffi::OsStr; + /// + /// let mut components = Path::new("/tmp/foo.txt").components(); + /// + /// assert_eq!(components.next(), Some(Component::RootDir)); + /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("tmp")))); + /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("foo.txt")))); + /// assert_eq!(components.next(), None) + /// ``` + /// + /// [`CurDir`]: Component::CurDir + #[stable(feature = "rust1", since = "1.0.0")] + pub fn components(&self) -> Components<'_> { + let prefix = parse_prefix(self.as_os_str()); + Components { + path: self.as_u8_slice(), + prefix, + has_physical_root: has_physical_root(self.as_u8_slice(), prefix), + // use a platform-specific initial state to avoid one turn of + // the state-machine when the platform doesn't have a Prefix. + front: const { if HAS_PREFIXES { State::Prefix } else { State::StartDir } }, + back: State::Body, + } + } + + /// Produces an iterator over the path's components viewed as [`OsStr`] + /// slices. + /// + /// For more information about the particulars of how the path is separated + /// into components, see [`components`]. + /// + /// [`components`]: Path::components + /// + /// # Examples + /// + /// ``` + /// use std::path::{self, Path}; + /// use std::ffi::OsStr; + /// + /// let mut it = Path::new("/tmp/foo.txt").iter(); + /// assert_eq!(it.next(), Some(OsStr::new(&path::MAIN_SEPARATOR.to_string()))); + /// assert_eq!(it.next(), Some(OsStr::new("tmp"))); + /// assert_eq!(it.next(), Some(OsStr::new("foo.txt"))); + /// assert_eq!(it.next(), None) + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn iter(&self) -> Iter<'_> { + Iter { inner: self.components() } + } + + /// Returns an object that implements [`Display`] for safely printing paths + /// that may contain non-Unicode data. This may perform lossy conversion, + /// depending on the platform. If you would like an implementation which + /// escapes the path please use [`Debug`] instead. + /// + /// [`Display`]: fmt::Display + /// [`Debug`]: fmt::Debug + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("/tmp/foo.rs"); + /// + /// println!("{}", path.display()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this does not display the path, \ + it returns an object that can be displayed"] + #[inline] + pub fn display(&self) -> Display<'_> { + Display { inner: self.inner.display() } + } + + /// Returns the same path as `&Path`. + /// + /// This method is redundant when used directly on `&Path`, but + /// it helps dereferencing other `PathBuf`-like types to `Path`s, + /// for example references to `Box` or `Arc`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_path(&self) -> &Path { + self + } + + // /// Queries the file system to get information about a file, directory, etc. + // /// + // /// This function will traverse symbolic links to query information about the + // /// destination file. + // /// + // /// This is an alias to [`fs::metadata`]. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// + // /// let path = Path::new("/Minas/tirith"); + // /// let metadata = path.metadata().expect("metadata call failed"); + // /// println!("{:?}", metadata.file_type()); + // /// ``` + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[inline] + // pub fn metadata(&self) -> io::Result { + // fs::metadata(self) + // } + + // /// Queries the metadata about a file without following symlinks. + // /// + // /// This is an alias to [`fs::symlink_metadata`]. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// + // /// let path = Path::new("/Minas/tirith"); + // /// let metadata = path.symlink_metadata().expect("symlink_metadata call failed"); + // /// println!("{:?}", metadata.file_type()); + // /// ``` + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[inline] + // pub fn symlink_metadata(&self) -> io::Result { + // fs::symlink_metadata(self) + // } + + // /// Returns the canonical, absolute form of the path with all intermediate + // /// components normalized and symbolic links resolved. + // /// + // /// This is an alias to [`fs::canonicalize`]. + // /// + // /// # Errors + // /// + // /// This method will return an error in the following situations, but is not + // /// limited to just these cases: + // /// + // /// * `path` does not exist. + // /// * A non-final component in path is not a directory. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::{Path, PathBuf}; + // /// + // /// let path = Path::new("/foo/test/../test/bar.rs"); + // /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); + // /// ``` + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[inline] + // pub fn canonicalize(&self) -> io::Result { + // fs::canonicalize(self) + // } + + /// Normalize a path, including `..` without traversing the filesystem. + /// + /// Returns an error if normalization would leave leading `..` components. + /// + ///

+ /// + /// This function always resolves `..` to the "lexical" parent. + /// That is "a/b/../c" will always resolve to `a/c` which can change the meaning of the path. + /// In particular, `a/c` and `a/b/../c` are distinct on many systems because `b` may be a symbolic link, so its parent isn't `a`. + /// + ///
+ /// + /// [`path::absolute`](absolute) is an alternative that preserves `..`. + /// Or [`Path::canonicalize`] can be used to resolve any `..` by querying the filesystem. + #[unstable(feature = "normalize_lexically", issue = "134694")] + pub fn normalize_lexically(&self) -> Result { + let mut lexical = PathBuf::new(); + let mut iter = self.components().peekable(); + + // Find the root, if any, and add it to the lexical path. + // Here we treat the Windows path "C:\" as a single "root" even though + // `components` splits it into two: (Prefix, RootDir). + let root = match iter.peek() { + Some(Component::ParentDir) => return Err(NormalizeError), + Some(p @ Component::RootDir) | Some(p @ Component::CurDir) => { + lexical.push(p); + iter.next(); + lexical.as_os_str().len() + } + Some(Component::Prefix(prefix)) => { + lexical.push(prefix.as_os_str()); + iter.next(); + if let Some(p @ Component::RootDir) = iter.peek() { + lexical.push(p); + iter.next(); + } + lexical.as_os_str().len() + } + None => return Ok(PathBuf::new()), + Some(Component::Normal(_)) => 0, + }; + + for component in iter { + match component { + Component::RootDir => unreachable!(), + Component::Prefix(_) => return Err(NormalizeError), + Component::CurDir => continue, + Component::ParentDir => { + // It's an error if ParentDir causes us to go above the "root". + if lexical.as_os_str().len() == root { + return Err(NormalizeError); + } else { + lexical.pop(); + } + } + Component::Normal(path) => lexical.push(path), + } + } + Ok(lexical) + } + + // /// Reads a symbolic link, returning the file that the link points to. + // /// + // /// This is an alias to [`fs::read_link`]. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// + // /// let path = Path::new("/laputa/sky_castle.rs"); + // /// let path_link = path.read_link().expect("read_link call failed"); + // /// ``` + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[inline] + // pub fn read_link(&self) -> io::Result { + // fs::read_link(self) + // } + + // /// Returns an iterator over the entries within a directory. + // /// + // /// The iterator will yield instances of [io::Result]<[fs::DirEntry]>. New + // /// errors may be encountered after an iterator is initially constructed. + // /// + // /// This is an alias to [`fs::read_dir`]. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// + // /// let path = Path::new("/laputa"); + // /// for entry in path.read_dir().expect("read_dir call failed") { + // /// if let Ok(entry) = entry { + // /// println!("{:?}", entry.path()); + // /// } + // /// } + // /// ``` + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[inline] + // pub fn read_dir(&self) -> io::Result { + // fs::read_dir(self) + // } + + // /// Returns `true` if the path points at an existing entity. + // /// + // /// Warning: this method may be error-prone, consider using [`try_exists()`] instead! + // /// It also has a risk of introducing time-of-check to time-of-use ([TOCTOU]) bugs. + // /// + // /// This function will traverse symbolic links to query information about the + // /// destination file. + // /// + // /// If you cannot access the metadata of the file, e.g. because of a + // /// permission error or broken symbolic links, this will return `false`. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// assert!(!Path::new("does_not_exist.txt").exists()); + // /// ``` + // /// + // /// # See Also + // /// + // /// This is a convenience function that coerces errors to false. If you want to + // /// check errors, call [`Path::try_exists`]. + // /// + // /// [`try_exists()`]: Self::try_exists + // /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[must_use] + // #[inline] + // pub fn exists(&self) -> bool { + // fs::metadata(self).is_ok() + // } + + // /// Returns `Ok(true)` if the path points at an existing entity. + // /// + // /// This function will traverse symbolic links to query information about the + // /// destination file. In case of broken symbolic links this will return `Ok(false)`. + // /// + // /// [`Path::exists()`] only checks whether or not a path was both found and readable. By + // /// contrast, `try_exists` will return `Ok(true)` or `Ok(false)`, respectively, if the path + // /// was _verified_ to exist or not exist. If its existence can neither be confirmed nor + // /// denied, it will propagate an `Err(_)` instead. This can be the case if e.g. listing + // /// permission is denied on one of the parent directories. + // /// + // /// Note that while this avoids some pitfalls of the `exists()` method, it still can not + // /// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios + // /// where those bugs are not an issue. + // /// + // /// This is an alias for [`std::fs::exists`](crate::fs::exists). + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt")); + // /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err()); + // /// ``` + // /// + // /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou + // /// [`exists()`]: Self::exists + // #[stable(feature = "path_try_exists", since = "1.63.0")] + // #[inline] + // pub fn try_exists(&self) -> io::Result { + // fs::exists(self) + // } + + // /// Returns `true` if the path exists on disk and is pointing at a regular file. + // /// + // /// This function will traverse symbolic links to query information about the + // /// destination file. + // /// + // /// If you cannot access the metadata of the file, e.g. because of a + // /// permission error or broken symbolic links, this will return `false`. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// assert_eq!(Path::new("./is_a_directory/").is_file(), false); + // /// assert_eq!(Path::new("a_file.txt").is_file(), true); + // /// ``` + // /// + // /// # See Also + // /// + // /// This is a convenience function that coerces errors to false. If you want to + // /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call + // /// [`fs::Metadata::is_file`] if it was [`Ok`]. + // /// + // /// When the goal is simply to read from (or write to) the source, the most + // /// reliable way to test the source can be read (or written to) is to open + // /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on + // /// a Unix-like system for example. See [`fs::File::open`] or + // /// [`fs::OpenOptions::open`] for more information. + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[must_use] + // pub fn is_file(&self) -> bool { + // fs::metadata(self).map(|m| m.is_file()).unwrap_or(false) + // } + + // /// Returns `true` if the path exists on disk and is pointing at a directory. + // /// + // /// This function will traverse symbolic links to query information about the + // /// destination file. + // /// + // /// If you cannot access the metadata of the file, e.g. because of a + // /// permission error or broken symbolic links, this will return `false`. + // /// + // /// # Examples + // /// + // /// ```no_run + // /// use std::path::Path; + // /// assert_eq!(Path::new("./is_a_directory/").is_dir(), true); + // /// assert_eq!(Path::new("a_file.txt").is_dir(), false); + // /// ``` + // /// + // /// # See Also + // /// + // /// This is a convenience function that coerces errors to false. If you want to + // /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call + // /// [`fs::Metadata::is_dir`] if it was [`Ok`]. + // #[stable(feature = "path_ext", since = "1.5.0")] + // #[must_use] + // pub fn is_dir(&self) -> bool { + // fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) + // } + + // /// Returns `true` if the path exists on disk and is pointing at a symbolic link. + // /// + // /// This function will not traverse symbolic links. + // /// In case of a broken symbolic link this will also return true. + // /// + // /// If you cannot access the directory containing the file, e.g., because of a + // /// permission error, this will return false. + // /// + // /// # Examples + // /// + // /// ```rust,no_run + // /// # #[cfg(unix)] { + // /// use std::path::Path; + // /// use std::os::unix::fs::symlink; + // /// + // /// let link_path = Path::new("link"); + // /// symlink("/origin_does_not_exist/", link_path).unwrap(); + // /// assert_eq!(link_path.is_symlink(), true); + // /// assert_eq!(link_path.exists(), false); + // /// # } + // /// ``` + // /// + // /// # See Also + // /// + // /// This is a convenience function that coerces errors to false. If you want to + // /// check errors, call [`fs::symlink_metadata`] and handle its [`Result`]. Then call + // /// [`fs::Metadata::is_symlink`] if it was [`Ok`]. + // #[must_use] + // #[stable(feature = "is_symlink", since = "1.58.0")] + // pub fn is_symlink(&self) -> bool { + // fs::symlink_metadata(self).map(|m| m.is_symlink()).unwrap_or(false) + // } + + /// Converts a [`Box`](Box) into a [`PathBuf`] without copying or + /// allocating. + #[stable(feature = "into_boxed_path", since = "1.20.0")] + #[must_use = "`self` will be dropped if the result is not used"] + pub fn into_path_buf(self: Box) -> PathBuf { + let rw = Box::into_raw(self) as *mut OsStr; + let inner = unsafe { Box::from_raw(rw) }; + PathBuf { inner: OsString::from(inner) } + } +} + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for Path { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: Path is just a transparent wrapper around OsStr + unsafe { self.inner.clone_to_uninit(dst) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef for Path { + #[inline] + fn as_ref(&self) -> &OsStr { + &self.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Path { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, formatter) + } +} + +/// Helper struct for safely printing paths with [`format!`] and `{}`. +/// +/// A [`Path`] might contain non-Unicode data. This `struct` implements the +/// [`Display`] trait in a way that mitigates that. It is created by the +/// [`display`](Path::display) method on [`Path`]. This may perform lossy +/// conversion, depending on the platform. If you would like an implementation +/// which escapes the path please use [`Debug`] instead. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// +/// let path = Path::new("/tmp/foo.rs"); +/// +/// println!("{}", path.display()); +/// ``` +/// +/// [`Display`]: fmt::Display +/// [`format!`]: crate::format +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Display<'a> { + inner: os_str::Display<'a>, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for Path { + #[inline] + fn eq(&self, other: &Path) -> bool { + self.components() == other.components() + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for Path { + #[inline] + fn eq(&self, other: &str) -> bool { + let other: &OsStr = other.as_ref(); + self == other + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for str { + #[inline] + fn eq(&self, other: &Path) -> bool { + other == self + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for Path { + #[inline] + fn eq(&self, other: &String) -> bool { + self == other.as_str() + } +} + +#[stable(feature = "eq_str_for_path", since = "1.91.0")] +impl cmp::PartialEq for String { + #[inline] + fn eq(&self, other: &Path) -> bool { + self.as_str() == other + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Path { + fn hash(&self, h: &mut H) { + let bytes = self.as_u8_slice(); + let (prefix_len, verbatim) = match parse_prefix(&self.inner) { + Some(prefix) => { + prefix.hash(h); + (prefix.len(), prefix.is_verbatim()) + } + None => (0, false), + }; + let bytes = &bytes[prefix_len..]; + + let mut component_start = 0; + // track some extra state to avoid prefix collisions. + // ["foo", "bar"] and ["foobar"], will have the same payload bytes + // but result in different chunk_bits + let mut chunk_bits: usize = 0; + + for i in 0..bytes.len() { + let is_sep = if verbatim { is_verbatim_sep(bytes[i]) } else { is_sep_byte(bytes[i]) }; + if is_sep { + if i > component_start { + let to_hash = &bytes[component_start..i]; + chunk_bits = chunk_bits.wrapping_add(to_hash.len()); + chunk_bits = chunk_bits.rotate_right(2); + h.write(to_hash); + } + + // skip over separator and optionally a following CurDir item + // since components() would normalize these away. + component_start = i + 1; + + let tail = &bytes[component_start..]; + + if !verbatim { + component_start += match tail { + [b'.'] => 1, + [b'.', sep, ..] if is_sep_byte(*sep) => 1, + _ => 0, + }; + } + } + } + + if component_start < bytes.len() { + let to_hash = &bytes[component_start..]; + chunk_bits = chunk_bits.wrapping_add(to_hash.len()); + chunk_bits = chunk_bits.rotate_right(2); + h.write(to_hash); + } + + h.write_usize(chunk_bits); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Path {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Path { + #[inline] + fn partial_cmp(&self, other: &Path) -> Option { + Some(compare_components(self.components(), other.components())) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Path { + #[inline] + fn cmp(&self, other: &Path) -> cmp::Ordering { + compare_components(self.components(), other.components()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef for Path { + #[inline] + fn as_ref(&self) -> &Path { + self + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const AsRef for OsStr { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "cow_os_str_as_ref_path", since = "1.8.0")] +impl AsRef for Cow<'_, OsStr> { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for OsString { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for String { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRef for PathBuf { + #[inline] + fn as_ref(&self) -> &Path { + self + } +} + +#[stable(feature = "path_into_iter", since = "1.6.0")] +impl<'a> IntoIterator for &'a PathBuf { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; + #[inline] + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +#[stable(feature = "path_into_iter", since = "1.6.0")] +impl<'a> IntoIterator for &'a Path { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; + #[inline] + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +macro_rules! impl_cmp { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "partialeq_path", since = "1.6.0")] + impl PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other) + } + } + + #[stable(feature = "partialeq_path", since = "1.6.0")] + impl PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self, other) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self, other) + } + } + }; +} + +impl_cmp!(PathBuf, Path); +impl_cmp!(PathBuf, &Path); +impl_cmp!(Cow<'_, Path>, Path); +impl_cmp!(Cow<'_, Path>, &Path); +impl_cmp!(Cow<'_, Path>, PathBuf); + +macro_rules! impl_cmp_os_str { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "cmp_path", since = "1.8.0")] + impl PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other.as_ref()) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self.as_ref(), other) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other.as_ref()) + } + } + + #[stable(feature = "cmp_path", since = "1.8.0")] + impl PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self.as_ref(), other) + } + } + }; +} + +impl_cmp_os_str!(PathBuf, OsStr); +impl_cmp_os_str!(PathBuf, &OsStr); +impl_cmp_os_str!(PathBuf, Cow<'_, OsStr>); +impl_cmp_os_str!(PathBuf, OsString); +impl_cmp_os_str!(Path, OsStr); +impl_cmp_os_str!(Path, &OsStr); +impl_cmp_os_str!(Path, Cow<'_, OsStr>); +impl_cmp_os_str!(Path, OsString); +impl_cmp_os_str!(&Path, OsStr); +impl_cmp_os_str!(&Path, Cow<'_, OsStr>); +impl_cmp_os_str!(&Path, OsString); +impl_cmp_os_str!(Cow<'_, Path>, OsStr); +impl_cmp_os_str!(Cow<'_, Path>, &OsStr); +impl_cmp_os_str!(Cow<'_, Path>, OsString); + +#[stable(since = "1.7.0", feature = "strip_prefix")] +impl fmt::Display for StripPrefixError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "prefix not found".fmt(f) + } +} + +#[stable(since = "1.7.0", feature = "strip_prefix")] +impl Error for StripPrefixError {} + +#[unstable(feature = "normalize_lexically", issue = "134694")] +impl fmt::Display for NormalizeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("parent reference `..` points outside of base directory") + } +} +#[unstable(feature = "normalize_lexically", issue = "134694")] +impl Error for NormalizeError {} + +/// Makes the path absolute without accessing the filesystem. +/// +/// If the path is relative, the current directory is used as the base directory. +/// All intermediate components will be resolved according to platform-specific +/// rules, but unlike [`canonicalize`][crate::fs::canonicalize], this does not +/// resolve symlinks and may succeed even if the path does not exist. +/// +/// If the `path` is empty or getting the +/// [current directory][crate::env::current_dir] fails, then an error will be +/// returned. +/// +/// # Platform-specific behavior +/// +/// On POSIX platforms, the path is resolved using [POSIX semantics][posix-semantics], +/// except that it stops short of resolving symlinks. This means it will keep `..` +/// components and trailing separators. +/// +/// On Windows, for verbatim paths, this will simply return the path as given. For other +/// paths, this is currently equivalent to calling +/// [`GetFullPathNameW`][windows-path]. +/// +/// On Cygwin, this is currently equivalent to calling [`cygwin_conv_path`][cygwin-path] +/// with mode `CCP_WIN_A_TO_POSIX`, and then being processed like other POSIX platforms. +/// If a Windows path is given, it will be converted to an absolute POSIX path without +/// keeping `..`. +/// +/// Note that these [may change in the future][changes]. +/// +/// # Errors +/// +/// This function may return an error in the following situations: +/// +/// * If `path` is syntactically invalid; in particular, if it is empty. +/// * If getting the [current directory][crate::env::current_dir] fails. +/// +/// # Examples +/// +/// ## POSIX paths +/// +/// ``` +/// # #[cfg(unix)] +/// fn main() -> std::io::Result<()> { +/// use std::path::{self, Path}; +/// +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with("foo/bar")); +/// +/// // Absolute to absolute +/// let absolute = path::absolute("/foo//test/.././bar.rs")?; +/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs")); +/// Ok(()) +/// } +/// # #[cfg(not(unix))] +/// # fn main() {} +/// ``` +/// +/// ## Windows paths +/// +/// ``` +/// # #[cfg(windows)] +/// fn main() -> std::io::Result<()> { +/// use std::path::{self, Path}; +/// +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with(r"foo\bar")); +/// +/// // Absolute to absolute +/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?; +/// +/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs")); +/// Ok(()) +/// } +/// # #[cfg(not(windows))] +/// # fn main() {} +/// ``` +/// +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 +/// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew +/// [cygwin-path]: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html +#[stable(feature = "absolute_path", since = "1.79.0")] +pub fn absolute>(path: P) -> io::Result { + let path = path.as_ref(); + if path.as_os_str().is_empty() { + Err(io::const_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute")) + } else { + sys::path::absolute(path) + } +} diff --git a/crates/std/src/sys/cmath.rs b/crates/std/src/sys/cmath.rs new file mode 100644 index 0000000..1592218 --- /dev/null +++ b/crates/std/src/sys/cmath.rs @@ -0,0 +1,114 @@ +#![cfg(not(test))] + +// These symbols are all defined by `libm`, +// or by `compiler-builtins` on unsupported platforms. +unsafe extern "C" { + pub safe fn acos(n: f64) -> f64; + pub safe fn asin(n: f64) -> f64; + pub safe fn atan(n: f64) -> f64; + pub safe fn atan2(a: f64, b: f64) -> f64; + pub safe fn cosh(n: f64) -> f64; + pub safe fn expm1(n: f64) -> f64; + pub safe fn expm1f(n: f32) -> f32; + #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + pub safe fn hypot(x: f64, y: f64) -> f64; + #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] + pub safe fn hypotf(x: f32, y: f32) -> f32; + pub safe fn log1p(n: f64) -> f64; + pub safe fn log1pf(n: f32) -> f32; + pub safe fn sinh(n: f64) -> f64; + pub safe fn tan(n: f64) -> f64; + pub safe fn tanh(n: f64) -> f64; + pub safe fn tgamma(n: f64) -> f64; + pub safe fn tgammaf(n: f32) -> f32; + pub safe fn lgamma_r(n: f64, s: &mut i32) -> f64; + #[cfg(not(target_os = "aix"))] + pub safe fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub safe fn erf(n: f64) -> f64; + pub safe fn erff(n: f32) -> f32; + pub safe fn erfc(n: f64) -> f64; + pub safe fn erfcf(n: f32) -> f32; + + pub safe fn acosf128(n: f128) -> f128; + pub safe fn asinf128(n: f128) -> f128; + pub safe fn atanf128(n: f128) -> f128; + pub safe fn atan2f128(a: f128, b: f128) -> f128; + pub safe fn cbrtf128(n: f128) -> f128; + pub safe fn coshf128(n: f128) -> f128; + pub safe fn expm1f128(n: f128) -> f128; + pub safe fn hypotf128(x: f128, y: f128) -> f128; + pub safe fn log1pf128(n: f128) -> f128; + pub safe fn sinhf128(n: f128) -> f128; + pub safe fn tanf128(n: f128) -> f128; + pub safe fn tanhf128(n: f128) -> f128; + pub safe fn tgammaf128(n: f128) -> f128; + pub safe fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + pub safe fn erff128(n: f128) -> f128; + pub safe fn erfcf128(n: f128) -> f128; +} + +cfg_select! { + all(target_os = "windows", target_env = "msvc", target_arch = "x86") => { + // On 32-bit x86 MSVC these functions aren't defined, so we just define shims + // which promote everything to f64, perform the calculation, and then demote + // back to f32. While not precisely correct should be "correct enough" for now. + #[inline] + pub fn acosf(n: f32) -> f32 { + f64::acos(n as f64) as f32 + } + + #[inline] + pub fn asinf(n: f32) -> f32 { + f64::asin(n as f64) as f32 + } + + #[inline] + pub fn atan2f(n: f32, b: f32) -> f32 { + f64::atan2(n as f64, b as f64) as f32 + } + + #[inline] + pub fn atanf(n: f32) -> f32 { + f64::atan(n as f64) as f32 + } + + #[inline] + pub fn coshf(n: f32) -> f32 { + f64::cosh(n as f64) as f32 + } + + #[inline] + pub fn sinhf(n: f32) -> f32 { + f64::sinh(n as f64) as f32 + } + + #[inline] + pub fn tanf(n: f32) -> f32 { + f64::tan(n as f64) as f32 + } + + #[inline] + pub fn tanhf(n: f32) -> f32 { + f64::tanh(n as f64) as f32 + } + } + _ => { + unsafe extern "C" { + pub safe fn acosf(n: f32) -> f32; + pub safe fn asinf(n: f32) -> f32; + pub safe fn atan2f(a: f32, b: f32) -> f32; + pub safe fn atanf(n: f32) -> f32; + pub safe fn coshf(n: f32) -> f32; + pub safe fn sinhf(n: f32) -> f32; + pub safe fn tanf(n: f32) -> f32; + pub safe fn tanhf(n: f32) -> f32; + } + } +} + +// On AIX, we don't have lgammaf_r only the f64 version, so we can +// use the f64 version lgamma_r +#[cfg(target_os = "aix")] +pub fn lgammaf_r(n: f32, s: &mut i32) -> f32 { + lgamma_r(n.into(), s) as f32 +} diff --git a/crates/std/src/sys/configure_builtins.rs b/crates/std/src/sys/configure_builtins.rs new file mode 100644 index 0000000..7cdf04e --- /dev/null +++ b/crates/std/src/sys/configure_builtins.rs @@ -0,0 +1,62 @@ +//! The configure builtins provides runtime support compiler-builtin features +//! which require dynamic initialization to work as expected, e.g. aarch64 +//! outline-atomics. + +/// Enable LSE atomic operations at startup, if supported. +/// +/// Linker sections are based on what [`ctor`] does, with priorities to run slightly before user +/// code: +/// +/// - Apple uses the section `__mod_init_func`, `mod_init_funcs` is needed to set +/// `S_MOD_INIT_FUNC_POINTERS`. There doesn't seem to be a way to indicate priorities. +/// - Windows uses `.CRT$XCT`, which is run before user constructors (these should use `.CRT$XCU`). +/// - ELF uses `.init_array` with a priority of 90, which runs before our `ARGV_INIT_ARRAY` +/// initializer (priority 99). Both are within the 0-100 implementation-reserved range, per docs +/// for the [`prio-ctor-dtor`] warning, and this matches compiler-rt's `CONSTRUCTOR_PRIORITY`. +/// +/// To save startup time, the initializer is only run if outline atomic routines from +/// compiler-builtins may be used. If LSE is known to be available then the calls are never +/// emitted, and if we build the C intrinsics then it has its own initializer using the symbol +/// `__aarch64_have_lse_atomics`. +/// +/// Initialization is done in a global constructor to so we get the same behavior regardless of +/// whether Rust's `init` is used, or if we are in a `dylib` or `no_main` situation (as opposed +/// to doing it as part of pre-main startup). This also matches C implementations. +/// +/// Ideally `core` would have something similar, but detecting the CPU features requires the +/// auxiliary vector from the OS. We do the initialization in `std` rather than as part of +/// `compiler-builtins` because a builtins->std dependency isn't possible, and inlining parts of +/// `std-detect` would be much messier. +/// +/// [`ctor`]: https://github.com/mmastrac/rust-ctor/blob/63382b833ddcbfb8b064f4e86bfa1ed4026ff356/shared/src/macros/mod.rs#L522-L534 +/// [`prio-ctor-dtor`]: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html +#[cfg(all( + target_arch = "aarch64", + target_feature = "outline-atomics", + not(target_feature = "lse"), + not(feature = "compiler-builtins-c"), +))] +#[used] +#[cfg_attr(target_vendor = "apple", unsafe(link_section = "__DATA,__mod_init_func,mod_init_funcs"))] +#[cfg_attr(target_os = "windows", unsafe(link_section = ".CRT$XCT"))] +#[cfg_attr( + not(any(target_vendor = "apple", target_os = "windows")), + unsafe(link_section = ".init_array.90") +)] +static RUST_LSE_INIT: extern "C" fn() = { + extern "C" fn init_lse() { + use crate::arch; + + // This is provided by compiler-builtins::aarch64_outline_atomics. + unsafe extern "C" { + fn __rust_enable_lse(); + } + + if arch::is_aarch64_feature_detected!("lse") { + unsafe { + __rust_enable_lse(); + } + } + } + init_lse +}; diff --git a/crates/std/src/sys/env/common.rs b/crates/std/src/sys/env/common.rs new file mode 100644 index 0000000..87e86e2 --- /dev/null +++ b/crates/std/src/sys/env/common.rs @@ -0,0 +1,31 @@ +use crate::ffi::OsString; +use crate::{fmt, vec}; + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +impl Env { + pub(super) fn new(env: Vec<(OsString, OsString)>) -> Self { + Env { iter: env.into_iter() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.as_slice()).finish() + } +} + +impl !Send for Env {} +impl !Sync for Env {} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} diff --git a/crates/std/src/sys/env/mod.rs b/crates/std/src/sys/env/mod.rs index 8985651..f281635 100644 --- a/crates/std/src/sys/env/mod.rs +++ b/crates/std/src/sys/env/mod.rs @@ -11,6 +11,7 @@ target_os = "uefi", target_os = "wasi", target_os = "xous", + target_os = "survos", ))] mod common; diff --git a/crates/std/src/sys/env_consts.rs b/crates/std/src/sys/env_consts.rs new file mode 100644 index 0000000..573f540 --- /dev/null +++ b/crates/std/src/sys/env_consts.rs @@ -0,0 +1,426 @@ +//! Constants associated with each target. + +// Replaces the #[else] gate with #[cfg(not(any(…)))] of all the other gates. +// This ensures that they must be mutually exclusive and do not have precedence +// like cfg_select!. +macro cfg_unordered( + $(#[cfg($cfg:meta)] $os:item)* + #[else] $fallback:item +) { + $(#[cfg($cfg)] $os)* + #[cfg(not(any($($cfg),*)))] $fallback +} + +// Keep entries sorted alphabetically and mutually exclusive. + +cfg_unordered! { + +#[cfg(target_os = "aix")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "aix"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".a"; + pub const DLL_EXTENSION: &str = "a"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "android")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "android"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "cygwin")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "cygwin"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".dll"; + pub const DLL_EXTENSION: &str = "dll"; + pub const EXE_SUFFIX: &str = ".exe"; + pub const EXE_EXTENSION: &str = "exe"; +} + +#[cfg(target_os = "dragonfly")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "dragonfly"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "emscripten")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; +} + +#[cfg(target_os = "espidf")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "espidf"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "freebsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "freebsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "fuchsia")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "fuchsia"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "haiku")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "haiku"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "hermit")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "hermit"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "horizon")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "horizon"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + +#[cfg(target_os = "hurd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "hurd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "illumos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "illumos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "ios")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "ios"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "l4re")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "l4re"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "linux")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "linux"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "macos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "macos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "netbsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "netbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "nto")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nto"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "nuttx")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nuttx"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "openbsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "openbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "redox")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "redox"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "rtems")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "rtems"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".sgxs"; + pub const DLL_EXTENSION: &str = "sgxs"; + pub const EXE_SUFFIX: &str = ".sgxs"; + pub const EXE_EXTENSION: &str = "sgxs"; +} + +#[cfg(target_os = "solaris")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "solaris"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "solid_asp3")] +pub mod os { + pub const FAMILY: &str = "itron"; + pub const OS: &str = "solid"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "tvos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "tvos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "uefi")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "uefi"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".efi"; + pub const EXE_EXTENSION: &str = "efi"; +} + +#[cfg(target_os = "vexos")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "vexos"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".bin"; + pub const EXE_EXTENSION: &str = "bin"; +} + +#[cfg(target_os = "visionos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "visionos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "vita")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "vita"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + +#[cfg(target_os = "vxworks")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "vxworks"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(all(target_family = "wasm", not(any(target_os = "emscripten", target_os = "linux"))))] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".wasm"; + pub const DLL_EXTENSION: &str = "wasm"; + pub const EXE_SUFFIX: &str = ".wasm"; + pub const EXE_EXTENSION: &str = "wasm"; +} + +#[cfg(target_os = "watchos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "watchos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "windows")] +pub mod os { + pub const FAMILY: &str = "windows"; + pub const OS: &str = "windows"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".dll"; + pub const DLL_EXTENSION: &str = "dll"; + pub const EXE_SUFFIX: &str = ".exe"; + pub const EXE_EXTENSION: &str = "exe"; +} + +#[cfg(target_os = "zkvm")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".elf"; + pub const DLL_EXTENSION: &str = "elf"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + +// The fallback when none of the other gates match. +#[else] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +} diff --git a/crates/std/src/sys/exit.rs b/crates/std/src/sys/exit.rs new file mode 100644 index 0000000..53fb92b --- /dev/null +++ b/crates/std/src/sys/exit.rs @@ -0,0 +1,143 @@ +cfg_select! { + target_os = "linux" => { + /// Mitigation for + /// + /// On glibc, `libc::exit` has been observed to not always be thread-safe. + /// It is currently unclear whether that is a glibc bug or allowed by the standard. + /// To mitigate this problem, we ensure that only one + /// Rust thread calls `libc::exit` (or returns from `main`) by calling this function before + /// calling `libc::exit` (or returning from `main`). + /// + /// Technically, this is not enough to ensure soundness, since other code directly calling + /// `libc::exit` will still race with this. + /// + /// *This function does not itself call `libc::exit`.* This is so it can also be used + /// to guard returning from `main`. + /// + /// This function will return only the first time it is called in a process. + /// + /// * If it is called again on the same thread as the first call, it will abort. + /// * If it is called again on a different thread, it will wait in a loop + /// (waiting for the process to exit). + pub fn unique_thread_exit() { + use crate::ffi::c_int; + use crate::ptr; + use crate::sync::atomic::AtomicPtr; + use crate::sync::atomic::Ordering::{Acquire, Relaxed}; + + static EXITING_THREAD_ID: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + + // We use the address of `errno` as a cheap and safe way to identify + // threads. As the C standard mandates that `errno` must have thread + // storage duration, we can rely on its address not changing over the + // lifetime of the thread. Additionally, accesses to `errno` are + // async-signal-safe, so this function is available in all imaginable + // circumstances. + let this_thread_id = crate::sys::io::errno_location(); + match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { + Ok(_) => { + // This is the first thread to call `unique_thread_exit`, + // and this is the first time it is called. Continue exiting. + } + Err(exiting_thread_id) if exiting_thread_id == this_thread_id => { + // This is the first thread to call `unique_thread_exit`, + // but this is the second time it is called. + // Abort the process. + core::panicking::panic_nounwind("std::process::exit called re-entrantly") + } + Err(_) => { + // This is not the first thread to call `unique_thread_exit`. + // Pause until the process exits. + loop { + // Safety: libc::pause is safe to call. + unsafe { libc::pause(); } + } + } + } + } + } + _ => { + /// Mitigation for + /// + /// Mitigation is ***NOT*** implemented on this platform, either because this platform + /// is not affected, or because mitigation is not yet implemented for this platform. + #[cfg_attr(any(test, doctest), expect(dead_code))] + pub fn unique_thread_exit() { + // Mitigation not required on platforms where `exit` is thread-safe. + } + } +} + +pub fn exit(code: i32) -> ! { + cfg_select! { + target_os = "hermit" => { + unsafe { hermit_abi::exit(code) } + } + target_os = "linux" => { + unsafe { + unique_thread_exit(); + libc::exit(code) + } + } + target_os = "motor" => { + moto_rt::process::exit(code) + } + all(target_vendor = "fortanix", target_env = "sgx") => { + crate::sys::pal::abi::exit_with_code(code as _) + } + target_os = "solid_asp3" => { + rtabort!("exit({}) called", code) + } + target_os = "teeos" => { + let _ = code; + panic!("TA should not call `exit`") + } + target_os = "uefi" => { + use r_efi::base::Status; + + use crate::os::uefi::env; + + if let (Some(boot_services), Some(handle)) = + (env::boot_services(), env::try_image_handle()) + { + let boot_services = boot_services.cast::(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } + crate::intrinsics::abort() + } + any( + target_family = "unix", + target_os = "wasi", + ) => { + unsafe { libc::exit(code as crate::ffi::c_int) } + } + target_os = "vexos" => { + let _ = code; + + unsafe { + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } + } + target_os = "windows" => { + unsafe { crate::sys::pal::c::ExitProcess(code as u32) } + } + target_os = "xous" => { + crate::os::xous::ffi::exit(code as u32) + } + _ => { + let _ = code; + crate::intrinsics::abort() + } + } +} diff --git a/crates/std/src/sys/fs/common.rs b/crates/std/src/sys/fs/common.rs new file mode 100644 index 0000000..4d47d39 --- /dev/null +++ b/crates/std/src/sys/fs/common.rs @@ -0,0 +1,81 @@ +#![allow(dead_code)] // not used on all platforms + +use crate::io::{self, Error, ErrorKind}; +use crate::path::{Path, PathBuf}; +use crate::sys::fs::{File, OpenOptions}; +use crate::sys::helpers::ignore_notfound; +use crate::{fmt, fs}; + +pub(crate) const NOT_FILE_ERROR: Error = io::const_error!( + ErrorKind::InvalidInput, + "the source path is neither a regular file nor a symlink to a regular file", +); + +pub fn copy(from: &Path, to: &Path) -> io::Result { + let mut reader = fs::File::open(from)?; + let metadata = reader.metadata()?; + + if !metadata.is_file() { + return Err(NOT_FILE_ERROR); + } + + let mut writer = fs::File::create(to)?; + let perm = metadata.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + writer.set_permissions(perm)?; + Ok(ret) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = fs::symlink_metadata(path)?.file_type(); + if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in fs::read_dir(path)? { + let result: io::Result<()> = try { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + fs::remove_file(&child.path())?; + } + }; + // ignore internal NotFound errors to prevent race conditions + if let Err(err) = &result + && err.kind() != io::ErrorKind::NotFound + { + return result; + } + } + ignore_notfound(fs::remove_dir(path)) +} + +pub fn exists(path: &Path) -> io::Result { + match fs::metadata(path) { + Ok(_) => Ok(true), + Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error), + } +} + +pub struct Dir { + path: PathBuf, +} + +impl Dir { + pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result { + path.canonicalize().map(|path| Self { path }) + } + + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { + File::open(&self.path.join(path), &opts) + } +} + +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Dir").field("path", &self.path).finish() + } +} diff --git a/crates/std/src/sys/fs/mod.rs b/crates/std/src/sys/fs/mod.rs new file mode 100644 index 0000000..0c297c5 --- /dev/null +++ b/crates/std/src/sys/fs/mod.rs @@ -0,0 +1,173 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::io; +use crate::path::{Path, PathBuf}; + +pub mod common; + +cfg_select! { + any(target_family = "unix", target_os = "wasi") => { + mod unix; + use unix as imp; + #[cfg(not(target_os = "wasi"))] + pub use unix::{chown, fchown, lchown, mkfifo}; + #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] + pub use unix::chroot; + #[cfg(not(target_os = "wasi"))] + pub(crate) use unix::debug_assert_fd_is_open; + #[cfg(any(target_os = "linux", target_os = "android"))] + pub(super) use unix::CachedFileMetadata; + use crate::sys::helpers::run_path_with_cstr as with_native_path; + } + target_os = "windows" => { + mod windows; + use windows as imp; + pub use windows::{symlink_inner, junction_point}; + use crate::sys::path::with_native_path; + } + target_os = "hermit" => { + mod hermit; + use hermit as imp; + } + target_os = "motor" => { + mod motor; + use motor as imp; + } + target_os = "solid_asp3" => { + mod solid; + use solid as imp; + } + target_os = "uefi" => { + mod uefi; + use uefi as imp; + } + target_os = "vexos" => { + mod vexos; + use vexos as imp; + } + _ => { + mod unsupported; + use unsupported as imp; + } +} + +// FIXME: Replace this with platform-specific path conversion functions. +#[cfg(not(any(target_family = "unix", target_os = "windows", target_os = "wasi")))] +#[inline] +pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> io::Result { + f(path) +} + +pub use imp::{ + Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions, + ReadDir, +}; + +pub fn read_dir(path: &Path) -> io::Result { + // FIXME: use with_native_path on all platforms + imp::readdir(path) +} + +pub fn remove_file(path: &Path) -> io::Result<()> { + with_native_path(path, &imp::unlink) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + with_native_path(old, &|old| with_native_path(new, &|new| imp::rename(old, new))) +} + +pub fn remove_dir(path: &Path) -> io::Result<()> { + with_native_path(path, &imp::rmdir) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::remove_dir_all(path); + #[cfg(windows)] + with_native_path(path, &imp::remove_dir_all) +} + +pub fn read_link(path: &Path) -> io::Result { + with_native_path(path, &imp::readlink) +} + +pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { + // FIXME: use with_native_path on all platforms + #[cfg(windows)] + return imp::symlink(original, link); + #[cfg(not(windows))] + with_native_path(original, &|original| { + with_native_path(link, &|link| imp::symlink(original, link)) + }) +} + +pub fn hard_link(original: &Path, link: &Path) -> io::Result<()> { + with_native_path(original, &|original| { + with_native_path(link, &|link| imp::link(original, link)) + }) +} + +pub fn metadata(path: &Path) -> io::Result { + with_native_path(path, &imp::stat) +} + +pub fn symlink_metadata(path: &Path) -> io::Result { + with_native_path(path, &imp::lstat) +} + +pub fn set_permissions(path: &Path, perm: FilePermissions) -> io::Result<()> { + with_native_path(path, &|path| imp::set_perm(path, perm.clone())) +} + +#[cfg(all(unix, not(target_os = "vxworks")))] +pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io::Result<()> { + use crate::fs::OpenOptions; + + let mut options = OpenOptions::new(); + + // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. + // Their filesystems do not have symbolic links, so no special handling is required. + #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] + { + use crate::os::unix::fs::OpenOptionsExt; + options.custom_flags(libc::O_NOFOLLOW); + } + + options.open(path)?.set_permissions(perm) +} + +#[cfg(any(not(unix), target_os = "vxworks"))] +pub fn set_permissions_nofollow(_path: &Path, _perm: crate::fs::Permissions) -> io::Result<()> { + crate::unimplemented!( + "`set_permissions_nofollow` is currently only implemented on Unix platforms" + ) +} + +pub fn canonicalize(path: &Path) -> io::Result { + with_native_path(path, &imp::canonicalize) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::copy(from, to); + #[cfg(windows)] + with_native_path(from, &|from| with_native_path(to, &|to| imp::copy(from, to))) +} + +pub fn exists(path: &Path) -> io::Result { + // FIXME: use with_native_path on all platforms + #[cfg(not(windows))] + return imp::exists(path); + #[cfg(windows)] + with_native_path(path, &imp::exists) +} + +pub fn set_times(path: &Path, times: FileTimes) -> io::Result<()> { + with_native_path(path, &|path| imp::set_times(path, times.clone())) +} + +pub fn set_times_nofollow(path: &Path, times: FileTimes) -> io::Result<()> { + with_native_path(path, &|path| imp::set_times_nofollow(path, times.clone())) +} diff --git a/crates/std/src/sys/fs/unsupported.rs b/crates/std/src/sys/fs/unsupported.rs new file mode 100644 index 0000000..069b4fb --- /dev/null +++ b/crates/std/src/sys/fs/unsupported.rs @@ -0,0 +1,362 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::fs::TryLockError; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +pub use crate::sys::fs::common::Dir; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +pub struct File(!); + +pub struct FileAttr(!); + +pub struct ReadDir(!); + +pub struct DirEntry(!); + +#[derive(Clone, Debug)] +pub struct OpenOptions {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +pub struct FilePermissions(!); + +pub struct FileType(!); + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.0 + } + + pub fn perm(&self) -> FilePermissions { + self.0 + } + + pub fn file_type(&self) -> FileType { + self.0 + } + + pub fn modified(&self) -> io::Result { + self.0 + } + + pub fn accessed(&self) -> io::Result { + self.0 + } + + pub fn created(&self) -> io::Result { + self.0 + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + self.0 + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.0 + } + + pub fn set_readonly(&mut self, _readonly: bool) { + self.0 + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + self.0 + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + self.0 + } +} + +impl Eq for FilePermissions {} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 + } + + pub fn is_file(&self) -> bool { + self.0 + } + + pub fn is_symlink(&self) -> bool { + self.0 + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + self.0 + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + self.0 + } +} + +impl Eq for FileType {} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + self.0 + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.0 + } + + pub fn file_name(&self) -> OsString { + self.0 + } + + pub fn metadata(&self) -> io::Result { + self.0 + } + + pub fn file_type(&self) -> io::Result { + self.0 + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions {} + } + + pub fn read(&mut self, _read: bool) {} + pub fn write(&mut self, _write: bool) {} + pub fn append(&mut self, _append: bool) {} + pub fn truncate(&mut self, _truncate: bool) {} + pub fn create(&mut self, _create: bool) {} + pub fn create_new(&mut self, _create_new: bool) {} +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + self.0 + } + + pub fn fsync(&self) -> io::Result<()> { + self.0 + } + + pub fn datasync(&self) -> io::Result<()> { + self.0 + } + + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> Result<(), TryLockError> { + self.0 + } + + pub fn try_lock_shared(&self) -> Result<(), TryLockError> { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + self.0 + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn flush(&self) -> io::Result<()> { + self.0 + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + self.0 + } + + pub fn size(&self) -> Option> { + self.0 + } + + pub fn tell(&self) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + self.0 + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(_path: &Path) -> io::Result { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/crates/std/src/sys/mod.rs b/crates/std/src/sys/mod.rs index 0b12d30..289cd25 100644 --- a/crates/std/src/sys/mod.rs +++ b/crates/std/src/sys/mod.rs @@ -1,5 +1,14 @@ +pub mod cmath; +pub mod configure_builtins; pub mod env; +pub mod env_consts; +pub mod exit; pub mod os_str; +pub mod path; +pub mod sync; +pub mod thread; +pub mod time; +// pub mod fs; /// A trait for viewing representations from std types. #[cfg_attr(not(target_os = "linux"), allow(unused))] diff --git a/crates/std/src/sys/path/mod.rs b/crates/std/src/sys/path/mod.rs new file mode 100644 index 0000000..254683b --- /dev/null +++ b/crates/std/src/sys/path/mod.rs @@ -0,0 +1,28 @@ +cfg_select! { + target_os = "windows" => { + mod windows; + mod windows_prefix; + pub use windows::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::*; + } + target_os = "solid_asp3" => { + mod unsupported_backslash; + pub use unsupported_backslash::*; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::*; + } + target_os = "cygwin" => { + mod cygwin; + mod windows_prefix; + pub use cygwin::*; + } + _ => { + mod unix; + pub use unix::*; + } +} diff --git a/crates/std/src/sys/path/unix.rs b/crates/std/src/sys/path/unix.rs new file mode 100644 index 0000000..0ee99c6 --- /dev/null +++ b/crates/std/src/sys/path/unix.rs @@ -0,0 +1,72 @@ +use crate::ffi::OsStr; +use crate::path::{Path, PathBuf, Prefix}; +use crate::{env, io}; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn parse_prefix(_: &OsStr) -> Option> { + None +} + +pub const HAS_PREFIXES: bool = false; +pub const MAIN_SEP_STR: &str = "/"; +pub const MAIN_SEP: char = '/'; + +/// Make a POSIX path absolute without changing its semantics. +pub(crate) fn absolute(path: &Path) -> io::Result { + // This is mostly a wrapper around collecting `Path::components`, with + // exceptions made where this conflicts with the POSIX specification. + // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017 + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + + // Get the components, skipping the redundant leading "." component if it exists. + let mut components = path.strip_prefix(".").unwrap_or(path).components(); + let path_os = path.as_os_str().as_encoded_bytes(); + + let mut normalized = if path.is_absolute() { + // "If a pathname begins with two successive characters, the + // first component following the leading characters may be + // interpreted in an implementation-defined manner, although more than + // two leading characters shall be treated as a single + // character." + if path_os.starts_with(b"//") && !path_os.starts_with(b"///") { + components.next(); + PathBuf::from("//") + } else { + PathBuf::new() + } + } else { + // env::current_dir()? + todo!() + }; + normalized.extend(components); + + // "Interfaces using pathname resolution may specify additional constraints + // when a pathname that does not name an existing directory contains at + // least one non- character and contains one or more trailing + // characters". + // A trailing is also meaningful if "a symbolic link is + // encountered during pathname resolution". + if path_os.ends_with(b"/") { + normalized.push(""); + } + + Ok(normalized) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + if cfg!(any(unix, target_os = "hermit", target_os = "wasi", target_os = "motor")) { + path.has_root() + } else { + path.has_root() && path.prefix().is_some() + } +} diff --git a/crates/std/src/sys/sync/condvar/mod.rs b/crates/std/src/sys/sync/condvar/mod.rs new file mode 100644 index 0000000..83cf0ae --- /dev/null +++ b/crates/std/src/sys/sync/condvar/mod.rs @@ -0,0 +1,44 @@ +cfg_select! { + any( + all(target_os = "windows", not(target_vendor="win7")), + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "motor", + target_os = "fuchsia", + all(target_family = "wasm", target_feature = "atomics"), + target_os = "hermit", + ) => { + mod futex; + pub use futex::Condvar; + } + any( + target_family = "unix", + target_os = "teeos", + ) => { + mod pthread; + pub use pthread::Condvar; + } + all(target_os = "windows", target_vendor = "win7") => { + mod windows7; + pub use windows7::Condvar; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::Condvar; + } + target_os = "solid_asp3" => { + mod itron; + pub use itron::Condvar; + } + target_os = "xous" => { + mod xous; + pub use xous::Condvar; + } + _ => { + mod no_threads; + pub use no_threads::Condvar; + } +} diff --git a/crates/std/src/sys/sync/condvar/no_threads.rs b/crates/std/src/sys/sync/condvar/no_threads.rs new file mode 100644 index 0000000..18d97d4 --- /dev/null +++ b/crates/std/src/sys/sync/condvar/no_threads.rs @@ -0,0 +1,27 @@ +use crate::sys::sync::Mutex; +use crate::thread::sleep; +use crate::time::Duration; + +pub struct Condvar {} + +impl Condvar { + #[inline] + pub const fn new() -> Condvar { + Condvar {} + } + + #[inline] + pub fn notify_one(&self) {} + + #[inline] + pub fn notify_all(&self) {} + + pub unsafe fn wait(&self, _mutex: &Mutex) { + panic!("condvar wait not supported") + } + + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, dur: Duration) -> bool { + sleep(dur); + false + } +} diff --git a/crates/std/src/sys/sync/mod.rs b/crates/std/src/sys/sync/mod.rs new file mode 100644 index 0000000..dbc543c --- /dev/null +++ b/crates/std/src/sys/sync/mod.rs @@ -0,0 +1,5 @@ +mod condvar; +mod mutex; + +pub use condvar::Condvar; +pub use mutex::Mutex; diff --git a/crates/std/src/sys/sync/mutex/mod.rs b/crates/std/src/sys/sync/mutex/mod.rs new file mode 100644 index 0000000..e3d6ad1 --- /dev/null +++ b/crates/std/src/sys/sync/mutex/mod.rs @@ -0,0 +1,47 @@ +cfg_select! { + any( + all(target_os = "windows", not(target_vendor = "win7")), + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "motor", + target_os = "dragonfly", + all(target_family = "wasm", target_feature = "atomics"), + target_os = "hermit", + ) => { + mod futex; + pub use futex::Mutex; + } + target_os = "fuchsia" => { + mod fuchsia; + pub use fuchsia::Mutex; + } + any( + target_family = "unix", + target_os = "teeos", + ) => { + mod pthread; + pub use pthread::Mutex; + } + all(target_os = "windows", target_vendor = "win7") => { + mod windows7; + pub use windows7::{Mutex, raw}; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::Mutex; + } + target_os = "solid_asp3" => { + mod itron; + pub use itron::Mutex; + } + target_os = "xous" => { + mod xous; + pub use xous::Mutex; + } + _ => { + mod no_threads; + pub use no_threads::Mutex; + } +} diff --git a/crates/std/src/sys/sync/mutex/no_threads.rs b/crates/std/src/sys/sync/mutex/no_threads.rs new file mode 100644 index 0000000..57c78f4 --- /dev/null +++ b/crates/std/src/sys/sync/mutex/no_threads.rs @@ -0,0 +1,31 @@ +use crate::cell::Cell; + +pub struct Mutex { + // This platform has no threads, so we can use a Cell here. + locked: Cell, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} // no threads on this platform + +impl Mutex { + #[inline] + pub const fn new() -> Mutex { + Mutex { locked: Cell::new(false) } + } + + #[inline] + pub fn lock(&self) { + assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex"); + } + + #[inline] + pub unsafe fn unlock(&self) { + self.locked.set(false); + } + + #[inline] + pub fn try_lock(&self) -> bool { + self.locked.replace(true) == false + } +} diff --git a/crates/std/src/sys/thread/mod.rs b/crates/std/src/sys/thread/mod.rs new file mode 100644 index 0000000..9816981 --- /dev/null +++ b/crates/std/src/sys/thread/mod.rs @@ -0,0 +1,154 @@ +cfg_select! { + target_os = "hermit" => { + mod hermit; + pub use hermit::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + target_os = "motor" => { + mod motor; + pub use motor::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + // SGX should protect in-enclave data from outside attackers, so there + // must not be any data leakage to the OS, particularly no 1-1 mapping + // between SGX thread names and OS thread names. Hence `set_name` is + // intentionally a no-op. + // + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic Rust thread code. This can be observed in + // the [`std::thread::tests::test_named_thread`] test, which succeeds + // as-is with the SGX target. + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, set_name}; + } + target_os = "solid_asp3" => { + mod solid; + pub use solid::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "teeos" => { + mod teeos; + pub use teeos::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::{available_parallelism, sleep}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + any(target_family = "unix", target_os = "wasi") => { + mod unix; + pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[cfg(not(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + target_os = "wasi", + )))] + pub use unix::set_name; + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "vxworks", + target_os = "wasi", + target_vendor = "apple", + ))] + pub use unix::sleep_until; + #[expect(dead_code)] + mod unsupported; + #[cfg(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + target_os = "wasi", + ))] + pub use unsupported::set_name; + } + target_os = "vexos" => { + mod vexos; + pub use vexos::{sleep, sleep_until, yield_now}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, DEFAULT_MIN_STACK_SIZE}; + } + all(target_family = "wasm", target_feature = "atomics") => { + mod wasm; + pub use wasm::sleep; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "windows" => { + mod windows; + pub use windows::{Thread, available_parallelism, current_os_id, set_name, set_name_wide, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "xous" => { + mod xous; + pub use xous::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + _ => { + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } +} + +#[cfg(not(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "vxworks", + target_os = "wasi", + target_vendor = "apple", + target_os = "motor", + target_os = "vexos" +)))] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::time::Instant; + + // The clock source used for `sleep` might not be the same used for `Instant`. + // Since this function *must not* return before the deadline, we recheck the + // time after every call to `sleep`. See #149935 for an example of this + // occurring on older Windows systems. + while let Some(delay) = deadline.checked_duration_since(Instant::now()) { + // Sleep for the estimated time remaining until the deadline. + // + // If your system has a better way of estimating the delay time or + // provides a way to sleep until an absolute time, specialize this + // function for your system. + sleep(delay); + } +} diff --git a/crates/std/src/sys/thread/unsupported.rs b/crates/std/src/sys/thread/unsupported.rs new file mode 100644 index 0000000..d633e83 --- /dev/null +++ b/crates/std/src/sys/thread/unsupported.rs @@ -0,0 +1,46 @@ +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZero; +use crate::thread::ThreadInit; +use crate::time::Duration; + +// Silence dead code warnings for the otherwise unused ThreadInit::init() call. +#[expect(dead_code)] +fn dummy_init_call(init: Box) { + drop(init.init()); +} + +pub struct Thread(!); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _init: Box) -> io::Result { + Err(io::Error::UNSUPPORTED_PLATFORM) + } + + pub fn join(self) { + self.0 + } +} + +pub fn available_parallelism() -> io::Result> { + Err(io::Error::UNKNOWN_THREAD_COUNT) +} + +pub fn current_os_id() -> Option { + None +} + +pub fn yield_now() { + // do nothing +} + +pub fn set_name(_name: &CStr) { + // nope +} + +pub fn sleep(_dur: Duration) { + panic!("can't sleep"); +} diff --git a/crates/std/src/sys/time/mod.rs b/crates/std/src/sys/time/mod.rs new file mode 100644 index 0000000..6cd1850 --- /dev/null +++ b/crates/std/src/sys/time/mod.rs @@ -0,0 +1,53 @@ +cfg_select! { + target_os = "hermit" => { + mod hermit; + use hermit as imp; + } + target_os = "motor" => { + use moto_rt::time as imp; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + use sgx as imp; + } + target_os = "solid_asp3" => { + mod solid; + use solid as imp; + } + target_os = "uefi" => { + mod uefi; + use uefi as imp; + } + any( + target_os = "teeos", + target_family = "unix", + target_os = "wasi", + ) => { + mod unix; + use unix as imp; + } + target_os = "vexos" => { + mod vexos; + #[expect(unused)] + mod unsupported; + + mod imp { + pub use super::vexos::Instant; + pub use super::unsupported::{SystemTime, UNIX_EPOCH}; + } + } + target_os = "windows" => { + mod windows; + use windows as imp; + } + target_os = "xous" => { + mod xous; + use xous as imp; + } + _ => { + mod unsupported; + use unsupported as imp; + } +} + +pub use imp::{Instant, SystemTime, UNIX_EPOCH}; diff --git a/crates/std/src/sys/time/unsupported.rs b/crates/std/src/sys/time/unsupported.rs new file mode 100644 index 0000000..9bdd572 --- /dev/null +++ b/crates/std/src/sys/time/unsupported.rs @@ -0,0 +1,49 @@ +use crate::time::Duration; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + panic!("time not implemented on this platform") + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub const MAX: SystemTime = SystemTime(Duration::MAX); + + pub const MIN: SystemTime = SystemTime(Duration::ZERO); + + pub fn now() -> SystemTime { + panic!("time not implemented on this platform") + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/crates/std/src/thread.rs b/crates/std/src/thread.rs new file mode 100644 index 0000000..45f8008 --- /dev/null +++ b/crates/std/src/thread.rs @@ -0,0 +1,13 @@ +pub use crate::sys::thread::*; + +pub struct ThreadInit { + pub handle: Thread, + pub rust_start: Box, +} +impl ThreadInit { + /// Initialize the 'current thread' mechanism on this thread, returning the + /// Rust entry point. + pub fn init(self: Box) -> Box { + todo!() + } +} diff --git a/crates/std/src/time.rs b/crates/std/src/time.rs new file mode 100644 index 0000000..1805d89 --- /dev/null +++ b/crates/std/src/time.rs @@ -0,0 +1,859 @@ +//! Temporal quantification. +//! +//! # Examples +//! +//! There are multiple ways to create a new [`Duration`]: +//! +//! ``` +//! # use std::time::Duration; +//! let five_seconds = Duration::from_secs(5); +//! assert_eq!(five_seconds, Duration::from_millis(5_000)); +//! assert_eq!(five_seconds, Duration::from_micros(5_000_000)); +//! assert_eq!(five_seconds, Duration::from_nanos(5_000_000_000)); +//! +//! let ten_seconds = Duration::from_secs(10); +//! let seven_nanos = Duration::from_nanos(7); +//! let total = ten_seconds + seven_nanos; +//! assert_eq!(total, Duration::new(10, 7)); +//! ``` +//! +//! Using [`Instant`] to calculate how long a function took to run: +//! +//! ```ignore (incomplete) +//! let now = Instant::now(); +//! +//! // Calling a slow function, it may take a while +//! slow_function(); +//! +//! let elapsed_time = now.elapsed(); +//! println!("Running slow_function() took {} seconds.", elapsed_time.as_secs()); +//! ``` + +#![stable(feature = "time", since = "1.3.0")] + +#[stable(feature = "time", since = "1.3.0")] +pub use core::time::Duration; +#[stable(feature = "duration_checked_float", since = "1.66.0")] +pub use core::time::TryFromFloatSecsError; + +use crate::error::Error; +use crate::fmt; +use crate::ops::{Add, AddAssign, Sub, SubAssign}; +use crate::sys::{FromInner, IntoInner, time}; + +/// A measurement of a monotonically nondecreasing clock. +/// Opaque and useful only with [`Duration`]. +/// +/// Instants are always guaranteed, barring [platform bugs], to be no less than any previously +/// measured instant when created, and are often useful for tasks such as measuring +/// benchmarks or timing how long an operation takes. +/// +/// Note, however, that instants are **not** guaranteed to be **steady**. In other +/// words, each tick of the underlying clock might not be the same length (e.g. +/// some seconds may be longer than others). An instant may jump forwards or +/// experience time dilation (slow down or speed up), but it will never go +/// backwards. +/// As part of this non-guarantee it is also not specified whether system suspends count as +/// elapsed time or not. The behavior varies across platforms and Rust versions. +/// +/// Instants are opaque types that can only be compared to one another. There is +/// no method to get "the number of seconds" from an instant. Instead, it only +/// allows measuring the duration between two instants (or comparing two +/// instants). +/// +/// The size of an `Instant` struct may vary depending on the target operating +/// system. +/// +/// Example: +/// +/// ```no_run +/// use std::time::{Duration, Instant}; +/// use std::thread::sleep; +/// +/// fn main() { +/// let now = Instant::now(); +/// +/// // we sleep for 2 seconds +/// sleep(Duration::new(2, 0)); +/// // it prints '2' +/// println!("{}", now.elapsed().as_secs()); +/// } +/// ``` +/// +/// [platform bugs]: Instant#monotonicity +/// +/// # OS-specific behaviors +/// +/// An `Instant` is a wrapper around system-specific types and it may behave +/// differently depending on the underlying operating system. For example, +/// the following snippet is fine on Linux but panics on macOS: +/// +/// ```no_run +/// use std::time::{Instant, Duration}; +/// +/// let now = Instant::now(); +/// let days_per_10_millennia = 365_2425; +/// let solar_seconds_per_day = 60 * 60 * 24; +/// let millennium_in_solar_seconds = 31_556_952_000; +/// assert_eq!(millennium_in_solar_seconds, days_per_10_millennia * solar_seconds_per_day / 10); +/// +/// let duration = Duration::new(millennium_in_solar_seconds, 0); +/// println!("{:?}", now + duration); +/// ``` +/// +/// For cross-platform code, you can comfortably use durations of up to around one hundred years. +/// +/// # Underlying System calls +/// +/// The following system calls are [currently] being used by `now()` to find out +/// the current time: +/// +/// | Platform | System call | +/// |-----------|----------------------------------------------------------------------| +/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | +/// | UNIX | [clock_gettime] with `CLOCK_MONOTONIC` | +/// | Darwin | [clock_gettime] with `CLOCK_UPTIME_RAW` | +/// | VXWorks | [clock_gettime] with `CLOCK_MONOTONIC` | +/// | SOLID | `get_tim` | +/// | WASI | [__wasi_clock_time_get] with `monotonic` | +/// | Windows | [QueryPerformanceCounter] | +/// +/// [currently]: crate::io#platform-specific-behavior +/// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter +/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time +/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode +/// [__wasi_clock_time_get]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get +/// [clock_gettime]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html +/// +/// **Disclaimer:** These system calls might change over time. +/// +/// > Note: mathematical operations like [`add`] may panic if the underlying +/// > structure cannot represent the new point in time. +/// +/// [`add`]: Instant::add +/// +/// ## Monotonicity +/// +/// On all platforms `Instant` will try to use an OS API that guarantees monotonic behavior +/// if available, which is the case for all [tier 1] platforms. +/// In practice such guarantees are – under rare circumstances – broken by hardware, virtualization +/// or operating system bugs. To work around these bugs and platforms not offering monotonic clocks +/// [`duration_since`], [`elapsed`] and [`sub`] saturate to zero. In older Rust versions this +/// lead to a panic instead. [`checked_duration_since`] can be used to detect and handle situations +/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order. +/// +/// This workaround obscures programming errors where earlier and later instants are accidentally +/// swapped. For this reason future Rust versions may reintroduce panics. +/// +/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html +/// [`duration_since`]: Instant::duration_since +/// [`elapsed`]: Instant::elapsed +/// [`sub`]: Instant::sub +/// [`checked_duration_since`]: Instant::checked_duration_since +/// +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[stable(feature = "time2", since = "1.8.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Instant")] +pub struct Instant(time::Instant); + +/// A measurement of the system clock, useful for talking to +/// external entities like the file system or other processes. +/// +/// Distinct from the [`Instant`] type, this time measurement **is not +/// monotonic**. This means that you can save a file to the file system, then +/// save another file to the file system, **and the second file has a +/// `SystemTime` measurement earlier than the first**. In other words, an +/// operation that happens after another operation in real time may have an +/// earlier `SystemTime`! +/// +/// Consequently, comparing two `SystemTime` instances to learn about the +/// duration between them returns a [`Result`] instead of an infallible [`Duration`] +/// to indicate that this sort of time drift may happen and needs to be handled. +/// +/// Although a `SystemTime` cannot be directly inspected, the [`UNIX_EPOCH`] +/// constant is provided in this module as an anchor in time to learn +/// information about a `SystemTime`. By calculating the duration from this +/// fixed point in time, a `SystemTime` can be converted to a human-readable time, +/// or perhaps some other string representation. +/// +/// The size of a `SystemTime` struct may vary depending on the target operating +/// system. +/// +/// A `SystemTime` does not count leap seconds. +/// `SystemTime::now()`'s behavior around a leap second +/// is the same as the operating system's wall clock. +/// The precise behavior near a leap second +/// (e.g. whether the clock appears to run slow or fast, or stop, or jump) +/// depends on platform and configuration, +/// so should not be relied on. +/// +/// Example: +/// +/// ```no_run +/// use std::time::{Duration, SystemTime}; +/// use std::thread::sleep; +/// +/// fn main() { +/// let now = SystemTime::now(); +/// +/// // we sleep for 2 seconds +/// sleep(Duration::new(2, 0)); +/// match now.elapsed() { +/// Ok(elapsed) => { +/// // it prints '2' +/// println!("{}", elapsed.as_secs()); +/// } +/// Err(e) => { +/// // the system clock went backwards! +/// println!("Great Scott! {e:?}"); +/// } +/// } +/// } +/// ``` +/// +/// # Platform-specific behavior +/// +/// The precision of `SystemTime` can depend on the underlying OS-specific time format. +/// For example, on Windows the time is represented in 100 nanosecond intervals whereas Linux +/// can represent nanosecond intervals. +/// +/// The following system calls are [currently] being used by `now()` to find out +/// the current time: +/// +/// | Platform | System call | +/// |-----------|----------------------------------------------------------------------| +/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | +/// | UNIX | [clock_gettime (Realtime Clock)] | +/// | Darwin | [clock_gettime (Realtime Clock)] | +/// | VXWorks | [clock_gettime (Realtime Clock)] | +/// | SOLID | `SOLID_RTC_ReadTime` | +/// | WASI | [__wasi_clock_time_get (Realtime Clock)] | +/// | Windows | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime] | +/// +/// [currently]: crate::io#platform-specific-behavior +/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time +/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode +/// [clock_gettime (Realtime Clock)]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html +/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get +/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime +/// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime +/// +/// **Disclaimer:** These system calls might change over time. +/// +/// > Note: mathematical operations like [`add`] may panic if the underlying +/// > structure cannot represent the new point in time. +/// +/// [`add`]: SystemTime::add +/// [`UNIX_EPOCH`]: SystemTime::UNIX_EPOCH +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[stable(feature = "time2", since = "1.8.0")] +pub struct SystemTime(time::SystemTime); + +/// An error returned from the `duration_since` and `elapsed` methods on +/// `SystemTime`, used to learn how far in the opposite direction a system time +/// lies. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread::sleep; +/// use std::time::{Duration, SystemTime}; +/// +/// let sys_time = SystemTime::now(); +/// sleep(Duration::from_secs(1)); +/// let new_sys_time = SystemTime::now(); +/// match sys_time.duration_since(new_sys_time) { +/// Ok(_) => {} +/// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()), +/// } +/// ``` +#[derive(Clone, Debug)] +#[stable(feature = "time2", since = "1.8.0")] +pub struct SystemTimeError(Duration); + +impl Instant { + /// Returns an instant corresponding to "now". + /// + /// # Examples + /// + /// ``` + /// use std::time::Instant; + /// + /// let now = Instant::now(); + /// ``` + #[must_use] + #[stable(feature = "time2", since = "1.8.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "instant_now")] + pub fn now() -> Instant { + Instant(time::Instant::now()) + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + /// + /// # Panics + /// + /// Previous Rust versions panicked when `earlier` was later than `self`. Currently this + /// method saturates. Future versions may reintroduce the panic in some circumstances. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity + /// + /// # Examples + /// + /// ```no_run + /// use std::time::{Duration, Instant}; + /// use std::thread::sleep; + /// + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.duration_since(now)); + /// println!("{:?}", now.duration_since(new_now)); // 0ns + /// ``` + #[must_use] + #[stable(feature = "time2", since = "1.8.0")] + pub fn duration_since(&self, earlier: Instant) -> Duration { + self.checked_duration_since(earlier).unwrap_or_default() + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or None if that instant is later than this one. + /// + /// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s, + /// this method can return `None`. + /// + /// [monotonicity bugs]: Instant#monotonicity + /// + /// # Examples + /// + /// ```no_run + /// use std::time::{Duration, Instant}; + /// use std::thread::sleep; + /// + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.checked_duration_since(now)); + /// println!("{:?}", now.checked_duration_since(new_now)); // None + /// ``` + #[must_use] + #[stable(feature = "checked_duration_since", since = "1.39.0")] + pub fn checked_duration_since(&self, earlier: Instant) -> Option { + self.0.checked_sub_instant(&earlier.0) + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::{Duration, Instant}; + /// use std::thread::sleep; + /// + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.saturating_duration_since(now)); + /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns + /// ``` + #[must_use] + #[stable(feature = "checked_duration_since", since = "1.39.0")] + pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { + self.checked_duration_since(earlier).unwrap_or_default() + } + + /// Returns the amount of time elapsed since this instant. + /// + /// # Panics + /// + /// Previous Rust versions panicked when the current time was earlier than self. Currently this + /// method returns a Duration of zero in that case. Future versions may reintroduce the panic. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity + /// + /// # Examples + /// + /// ```no_run + /// use std::thread::sleep; + /// use std::time::{Duration, Instant}; + /// + /// let instant = Instant::now(); + /// let three_secs = Duration::from_secs(3); + /// sleep(three_secs); + /// assert!(instant.elapsed() >= three_secs); + /// ``` + #[must_use] + #[stable(feature = "time2", since = "1.8.0")] + pub fn elapsed(&self) -> Duration { + Instant::now() - *self + } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add_duration(&duration).map(Instant) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_sub(&self, duration: Duration) -> Option { + self.0.checked_sub_duration(&duration).map(Instant) + } + + // Used by platform specific `sleep_until` implementations such as the one used on Linux. + #[cfg_attr( + not(target_os = "linux"), + allow(unused, reason = "not every platform has a specific `sleep_until`") + )] + pub(crate) fn into_inner(self) -> time::Instant { + self.0 + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Add for Instant { + type Output = Instant; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`Instant::checked_add`] for a version without panic. + fn add(self, other: Duration) -> Instant { + self.checked_add(other).expect("overflow when adding duration to instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Sub for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other).expect("overflow when subtracting duration from instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Sub for Instant { + type Output = Duration; + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + /// + /// # Panics + /// + /// Previous Rust versions panicked when `other` was later than `self`. Currently this + /// method saturates. Future versions may reintroduce the panic in some circumstances. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl SystemTime { + /// An anchor in time which can be used to create new `SystemTime` instances or + /// learn about where in time a `SystemTime` lies. + // + // NOTE! this documentation is duplicated, here and in std::time::UNIX_EPOCH. + // The two copies are not quite identical, because of the difference in naming. + /// + /// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with + /// respect to the system clock. Using `duration_since` on an existing + /// `SystemTime` instance can tell how far away from this point in time a + /// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a + /// `SystemTime` instance to represent another fixed point in time. + /// + /// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns + /// the number of non-leap seconds since the start of 1970 UTC. + /// This is a POSIX `time_t` (as a `u64`), + /// and is the same time representation as used in many Internet protocols. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::SystemTime; + /// + /// match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + /// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), + /// Err(_) => panic!("SystemTime before UNIX EPOCH!"), + /// } + /// ``` + #[stable(feature = "assoc_unix_epoch", since = "1.28.0")] + pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH; + + /// Represents the maximum value representable by [`SystemTime`] on this platform. + /// + /// This value differs a lot between platforms, but it is always the case + /// that any positive addition of a [`Duration`], whose value is greater + /// than or equal to the time precision of the operating system, to + /// [`SystemTime::MAX`] will fail. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(time_systemtime_limits)] + /// use std::time::{Duration, SystemTime}; + /// + /// // Adding zero will change nothing. + /// assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX)); + /// + /// // But adding just one second will already fail ... + /// // + /// // Keep in mind that this in fact may succeed, if the Duration is + /// // smaller than the time precision of the operating system, which + /// // happens to be 1ns on most operating systems, with Windows being the + /// // notable exception by using 100ns, hence why this example uses 1s. + /// assert_eq!(SystemTime::MAX.checked_add(Duration::new(1, 0)), None); + /// + /// // Utilize this for saturating arithmetic to improve error handling. + /// // In this case, we will use a certificate with a timestamp in the + /// // future as a practical example. + /// let configured_offset = Duration::from_secs(60 * 60 * 24); + /// let valid_after = + /// SystemTime::now() + /// .checked_add(configured_offset) + /// .unwrap_or(SystemTime::MAX); + /// ``` + #[unstable(feature = "time_systemtime_limits", issue = "149067")] + pub const MAX: SystemTime = SystemTime(time::SystemTime::MAX); + + /// Represents the minimum value representable by [`SystemTime`] on this platform. + /// + /// This value differs a lot between platforms, but it is always the case + /// that any positive subtraction of a [`Duration`] from, whose value is + /// greater than or equal to the time precision of the operating system, to + /// [`SystemTime::MIN`] will fail. + /// + /// Depending on the platform, this may be either less than or equal to + /// [`SystemTime::UNIX_EPOCH`], depending on whether the operating system + /// supports the representation of timestamps before the Unix epoch or not. + /// However, it is always guaranteed that a [`SystemTime::UNIX_EPOCH`] fits + /// between a [`SystemTime::MIN`] and [`SystemTime::MAX`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(time_systemtime_limits)] + /// use std::time::{Duration, SystemTime}; + /// + /// // Subtracting zero will change nothing. + /// assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN)); + /// + /// // But subtracting just one second will already fail. + /// // + /// // Keep in mind that this in fact may succeed, if the Duration is + /// // smaller than the time precision of the operating system, which + /// // happens to be 1ns on most operating systems, with Windows being the + /// // notable exception by using 100ns, hence why this example uses 1s. + /// assert_eq!(SystemTime::MIN.checked_sub(Duration::new(1, 0)), None); + /// + /// // Utilize this for saturating arithmetic to improve error handling. + /// // In this case, we will use a cache expiry as a practical example. + /// let configured_expiry = Duration::from_secs(60 * 3); + /// let expiry_threshold = + /// SystemTime::now() + /// .checked_sub(configured_expiry) + /// .unwrap_or(SystemTime::MIN); + /// ``` + #[unstable(feature = "time_systemtime_limits", issue = "149067")] + pub const MIN: SystemTime = SystemTime(time::SystemTime::MIN); + + /// Returns the system time corresponding to "now". + /// + /// # Examples + /// + /// ``` + /// use std::time::SystemTime; + /// + /// let sys_time = SystemTime::now(); + /// ``` + #[must_use] + #[stable(feature = "time2", since = "1.8.0")] + pub fn now() -> SystemTime { + SystemTime(time::SystemTime::now()) + } + + /// Returns the amount of time elapsed from an earlier point in time. + /// + /// This function may fail because measurements taken earlier are not + /// guaranteed to always be before later measurements (due to anomalies such + /// as the system clock being adjusted either forwards or backwards). + /// [`Instant`] can be used to measure elapsed time without this risk of failure. + /// + /// If successful, [Ok]\([Duration]) is returned where the duration represents + /// the amount of time elapsed from the specified measurement to this one. + /// + /// Returns an [`Err`] if `earlier` is later than `self`, and the error + /// contains how far from `self` the time is. + /// + /// # Examples + /// + /// ```no_run + /// use std::time::SystemTime; + /// + /// let sys_time = SystemTime::now(); + /// let new_sys_time = SystemTime::now(); + /// let difference = new_sys_time.duration_since(sys_time) + /// .expect("Clock may have gone backwards"); + /// println!("{difference:?}"); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn duration_since(&self, earlier: SystemTime) -> Result { + self.0.sub_time(&earlier.0).map_err(SystemTimeError) + } + + /// Returns the difference from this system time to the + /// current clock time. + /// + /// This function may fail as the underlying system clock is susceptible to + /// drift and updates (e.g., the system clock could go backwards), so this + /// function might not always succeed. If successful, [Ok]\([Duration]) is + /// returned where the duration represents the amount of time elapsed from + /// this time measurement to the current time. + /// + /// To measure elapsed time reliably, use [`Instant`] instead. + /// + /// Returns an [`Err`] if `self` is later than the current system time, and + /// the error contains how far from the current system time `self` is. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread::sleep; + /// use std::time::{Duration, SystemTime}; + /// + /// let sys_time = SystemTime::now(); + /// let one_sec = Duration::from_secs(1); + /// sleep(one_sec); + /// assert!(sys_time.elapsed().unwrap() >= one_sec); + /// ``` + #[stable(feature = "time2", since = "1.8.0")] + pub fn elapsed(&self) -> Result { + SystemTime::now().duration_since(*self) + } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + /// + /// In the case that the `duration` is smaller than the time precision of the operating + /// system, `Some(self)` will be returned. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add_duration(&duration).map(SystemTime) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + /// + /// In the case that the `duration` is smaller than the time precision of the operating + /// system, `Some(self)` will be returned. + #[stable(feature = "time_checked_add", since = "1.34.0")] + pub fn checked_sub(&self, duration: Duration) -> Option { + self.0.checked_sub_duration(&duration).map(SystemTime) + } + + /// Saturating [`SystemTime`] addition, computing `self + duration`, + /// returning [`SystemTime::MAX`] if overflow occurred. + /// + /// In the case that the `duration` is smaller than the time precision of + /// the operating system, `self` will be returned. + #[unstable(feature = "time_saturating_systemtime", issue = "151199")] + pub fn saturating_add(&self, duration: Duration) -> SystemTime { + self.checked_add(duration).unwrap_or(SystemTime::MAX) + } + + /// Saturating [`SystemTime`] subtraction, computing `self - duration`, + /// returning [`SystemTime::MIN`] if overflow occurred. + /// + /// In the case that the `duration` is smaller than the time precision of + /// the operating system, `self` will be returned. + #[unstable(feature = "time_saturating_systemtime", issue = "151199")] + pub fn saturating_sub(&self, duration: Duration) -> SystemTime { + self.checked_sub(duration).unwrap_or(SystemTime::MIN) + } + + /// Saturating computation of time elapsed from an earlier point in time, + /// returning [`Duration::ZERO`] in the case that `earlier` is later or + /// equal to `self`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(time_saturating_systemtime)] + /// use std::time::{Duration, SystemTime}; + /// + /// let now = SystemTime::now(); + /// let prev = now.saturating_sub(Duration::new(1, 0)); + /// + /// // now - prev should return non-zero. + /// assert_eq!(now.saturating_duration_since(prev), Duration::new(1, 0)); + /// assert!(now.duration_since(prev).is_ok()); + /// + /// // prev - now should return zero (and fail with the non-saturating). + /// assert_eq!(prev.saturating_duration_since(now), Duration::ZERO); + /// assert!(prev.duration_since(now).is_err()); + /// + /// // now - now should return zero (and work with the non-saturating). + /// assert_eq!(now.saturating_duration_since(now), Duration::ZERO); + /// assert!(now.duration_since(now).is_ok()); + /// ``` + #[unstable(feature = "time_saturating_systemtime", issue = "151199")] + pub fn saturating_duration_since(&self, earlier: SystemTime) -> Duration { + self.duration_since(earlier).unwrap_or(Duration::ZERO) + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Add for SystemTime { + type Output = SystemTime; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`SystemTime::checked_add`] for a version without panic. + fn add(self, dur: Duration) -> SystemTime { + self.checked_add(dur).expect("overflow when adding duration to instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl AddAssign for SystemTime { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Sub for SystemTime { + type Output = SystemTime; + + fn sub(self, dur: Duration) -> SystemTime { + self.checked_sub(dur).expect("overflow when subtracting duration from instant") + } +} + +#[stable(feature = "time_augmented_assignment", since = "1.9.0")] +impl SubAssign for SystemTime { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// An anchor in time which can be used to create new `SystemTime` instances or +/// learn about where in time a `SystemTime` lies. +// +// NOTE! this documentation is duplicated, here and in SystemTime::UNIX_EPOCH. +// The two copies are not quite identical, because of the difference in naming. +/// +/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with +/// respect to the system clock. Using `duration_since` on an existing +/// [`SystemTime`] instance can tell how far away from this point in time a +/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a +/// [`SystemTime`] instance to represent another fixed point in time. +/// +/// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns +/// the number of non-leap seconds since the start of 1970 UTC. +/// This is a POSIX `time_t` (as a `u64`), +/// and is the same time representation as used in many Internet protocols. +/// +/// # Examples +/// +/// ```no_run +/// use std::time::{SystemTime, UNIX_EPOCH}; +/// +/// match SystemTime::now().duration_since(UNIX_EPOCH) { +/// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), +/// Err(_) => panic!("SystemTime before UNIX EPOCH!"), +/// } +/// ``` +#[stable(feature = "time2", since = "1.8.0")] +pub const UNIX_EPOCH: SystemTime = SystemTime(time::UNIX_EPOCH); + +impl SystemTimeError { + /// Returns the positive duration which represents how far forward the + /// second system time was from the first. + /// + /// A `SystemTimeError` is returned from the [`SystemTime::duration_since`] + /// and [`SystemTime::elapsed`] methods whenever the second system time + /// represents a point later in time than the `self` of the method call. + /// + /// # Examples + /// + /// ```no_run + /// use std::thread::sleep; + /// use std::time::{Duration, SystemTime}; + /// + /// let sys_time = SystemTime::now(); + /// sleep(Duration::from_secs(1)); + /// let new_sys_time = SystemTime::now(); + /// match sys_time.duration_since(new_sys_time) { + /// Ok(_) => {} + /// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()), + /// } + /// ``` + #[must_use] + #[stable(feature = "time2", since = "1.8.0")] + pub fn duration(&self) -> Duration { + self.0 + } +} + +#[stable(feature = "time2", since = "1.8.0")] +impl Error for SystemTimeError {} + +#[stable(feature = "time2", since = "1.8.0")] +impl fmt::Display for SystemTimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "second time provided was later than self") + } +} + +impl FromInner for SystemTime { + fn from_inner(time: time::SystemTime) -> SystemTime { + SystemTime(time) + } +} + +impl IntoInner for SystemTime { + fn into_inner(self) -> time::SystemTime { + self.0 + } +} diff --git a/justfile b/justfile index ba32dd5..1705bbf 100644 --- a/justfile +++ b/justfile @@ -20,17 +20,40 @@ cp_std path: @sed -i -f patches.sed {{ "crates/std/src" / path }} update_std: - @# Not copied : sys/mod.rs, io.rs + @# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs + # @just cp_std "process.rs" + # @just cp_std "fs.rs" + @just cp_std "time.rs" + @just cp_std "num/mod.rs" @just cp_std "ffi/c_str.rs" @just cp_std "ffi/mod.rs" @just cp_std "ffi/os_str.rs" @just cp_std "ffi/os_str/tests.rs" + @just cp_std "sys/exit.rs" + @just cp_std "sys/env_consts.rs" + @just cp_std "sys/configure_builtins.rs" + @just cp_std "sys/cmath.rs" + @just cp_std "sys/sync/condvar/mod.rs" + @just cp_std "sys/sync/condvar/no_threads.rs" + @just cp_std "sys/sync/mutex/mod.rs" + @just cp_std "sys/sync/mutex/no_threads.rs" + @just cp_std "sys/thread/mod.rs" + @just cp_std "sys/thread/unsupported.rs" + @just cp_std "sys/time/mod.rs" + @just cp_std "sys/time/unsupported.rs" @just cp_std "sys/env/mod.rs" + @just cp_std "sys/env/common.rs" @just cp_std "sys/env/unsupported.rs" @just cp_std "sys/os_str/mod.rs" @just cp_std "sys/os_str/bytes.rs" @just cp_std "sys/os_str/bytes/tests.rs" + @just cp_std "sys/path/mod.rs" + @just cp_std "sys/fs/mod.rs" + @just cp_std "sys/fs/common.rs" + @just cp_std "sys/fs/unsupported.rs" @# Copied but edited for the moment + # @just cp_std "path.rs" + # @just cp_std "sys/path/unix.rs" # @just cp_std "hash/mod.rs" build_user_prog prog: diff --git a/patches.sed b/patches.sed index 921f281..987bc2d 100644 --- a/patches.sed +++ b/patches.sed @@ -1,6 +1,7 @@ s|crate::bstr::ByteStr|alloc::bstr::ByteStr|g s|crate::collections::TryReserveError|alloc::collections::TryReserveError|g s|crate::sync::Arc|alloc::sync::Arc|g +/target_os = "xous",/a \ target_os = "survos", # Ajouter d'autres modifications facilement ici : # s|ancien_texte|nouveau_texte|g diff --git a/riscv64.json b/riscv64.json index 7d21f3a..1801bfe 100644 --- a/riscv64.json +++ b/riscv64.json @@ -6,7 +6,7 @@ "target-endian": "little", "target-pointer-width": 64, "arch": "riscv64", - "os": "none", + "os": "survos", "vendor": "unknown", "env": "", "features": "+i,+m,+a,+zicsr",