From 6870f75cef3f480121e5316a228196b8bbaa838b Mon Sep 17 00:00:00 2001 From: dylni <46035563+dylni@users.noreply.github.com> Date: Fri, 3 Nov 2023 20:51:51 -0400 Subject: [PATCH] Increase MSRV to 1.74.0 --- .github/workflows/build.yml | 5 +- Cargo.toml | 4 +- README.md | 12 +- src/common/raw.rs | 42 +- src/lib.rs | 857 +++++++++++++++++------------------- src/raw_str.rs | 573 +++++++++--------------- src/wasm/raw.rs | 33 -- src/windows/raw.rs | 43 +- src/windows/wtf8/string.rs | 11 +- tests/raw_index.rs | 37 +- tests/raw_integration.rs | 4 - tests/raw_random.rs | 3 +- 12 files changed, 629 insertions(+), 995 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4785454..e667b7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,6 @@ name: build env: OS_STR_BYTES_CHECKED_CONVERSIONS: 1 - OS_STR_BYTES_NIGHTLY: 1 on: pull_request: @@ -27,7 +26,7 @@ jobs: cargo fmt --check shell: bash env: - cargo_hack_options: --feature-powerset --optional-deps --exclude-features nightly + cargo_hack_options: --feature-powerset --optional-deps timeout-minutes: 10 strategy: matrix: @@ -38,7 +37,6 @@ jobs: steps: - uses: dylni/build-actions/build-other@master with: - nightly-features: 'nightly print_bytes uniquote' target: ${{ matrix.target }} version: ${{ matrix.version }} timeout-minutes: 10 @@ -55,7 +53,6 @@ jobs: steps: - uses: dylni/build-actions/test@master with: - nightly-features: 'nightly print_bytes uniquote' version: ${{ matrix.version }} - run: cargo test --no-default-features --features raw_os_str timeout-minutes: 10 diff --git a/Cargo.toml b/Cargo.toml index 841b059..5116367 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "os_str_bytes" version = "6.6.1" authors = ["dylni"] edition = "2021" -rust-version = "1.61.0" +rust-version = "1.74.0" description = """ Convert between byte sequences and platform-native strings """ @@ -31,8 +31,6 @@ lazy_static = "1.4" [features] default = ["memchr", "raw_os_str"] -nightly = [] - checked_conversions = ["conversions"] conversions = [] raw_os_str = [] diff --git a/README.md b/README.md index 4c676bb..5255c63 100644 --- a/README.md +++ b/README.md @@ -47,32 +47,32 @@ The minimum supported Rust toolchain version depends on the platform: SOLID *-*-solid_asp3(-*) - 1.61.0 + 1.74.0 Unix Unix - 1.61.0 + 1.74.0 WASI *-wasi - 1.61.0 + 1.74.0 WebAssembly wasm32-*-unknown - 1.61.0 + 1.74.0 Windows *-*-windows-* - 1.61.0 + 1.74.0 Xous *-*-xous-* - 1.74 (rust-lang/rust#104101) + 1.74.0 diff --git a/src/common/raw.rs b/src/common/raw.rs index e9793b6..a7894e0 100644 --- a/src/common/raw.rs +++ b/src/common/raw.rs @@ -3,35 +3,6 @@ use std::fmt::Formatter; use crate::RawOsStr; -if_not_nightly! { - if_conversions! { - use super::Result; - } -} - -if_not_nightly! { - #[inline(always)] - pub(crate) const fn is_continuation(_: u8) -> bool { - false - } -} - -if_not_nightly! { - if_conversions! { - #[inline(always)] - pub(crate) fn validate_bytes(_: &[u8]) -> Result<()> { - Ok(()) - } - } -} - -if_not_nightly! { - #[inline(always)] - pub(crate) fn decode_code_point(_: &[u8]) -> u32 { - unreachable!(); - } -} - if_conversions! { pub(crate) fn ends_with(string: &[u8], suffix: &[u8]) -> bool { string.ends_with(suffix) @@ -44,17 +15,8 @@ if_conversions! { } } -fn as_inner(string: &RawOsStr) -> &[u8] { - if_nightly_return! { - { - string.as_encoded_bytes() - } - string.as_raw_bytes() - } -} - pub(crate) fn debug(string: &RawOsStr, f: &mut Formatter<'_>) -> fmt::Result { - for byte in as_inner(string) { + for byte in string.as_encoded_bytes() { write!(f, "\\x{:02X}", byte)?; } Ok(()) @@ -69,6 +31,6 @@ pub(crate) mod uniquote { use crate::RawOsStr; pub(crate) fn escape(string: &RawOsStr, f: &mut Formatter<'_>) -> Result { - super::as_inner(string).escape(f) + string.as_encoded_bytes().escape(f) } } diff --git a/src/lib.rs b/src/lib.rs index bf78685..122785c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,34 +116,6 @@ //! Provides implementations of [`uniquote::Quote`] for [`RawOsStr`] and //! [`RawOsString`]. //! -//! ### Nightly Features -//! -//! These features are unstable, since they rely on unstable Rust features. -//! -//! - **nightly** - -//! Changes the implementation to use the ["os\_str\_bytes" nightly -//! feature][feature] and provides: -//! - [`RawOsStr::as_encoded_bytes`] -//! - [`RawOsStr::as_os_str`] -//! - [`RawOsStr::from_encoded_bytes_unchecked`] -//! - [`RawOsStr::from_os_str`] -//! - [`RawOsString::from_encoded_vec_unchecked`] -//! - [`RawOsString::into_encoded_vec`] -//! - additional trait implementations for [`RawOsStr`] and [`RawOsString`] -//! -//! When applicable, a "Nightly Notes" section will be added to documentation -//! descriptions, indicating differences when this feature is enabled. -//! However, it will not cause any breaking changes. -//! -//! This feature will cause memory leaks for some newly deprecated methods. -//! Therefore, it is not recommended to use this feature until the next major -//! version, when those methods will be removed. However, it can be used to -//! prepare for upgrading and determine impact of the new feature. -//! -//! Because this feature should not be used in libraries, the -//! "OS_STR_BYTES_NIGHTLY" environment variable must be defined during -//! compilation. -//! //! # Implementation //! //! Some methods return [`Cow`] to account for platform differences. However, @@ -173,7 +145,7 @@ //! use std::env; //! use std::fs; //! -//! use os_str_bytes::RawOsStr; +//! use os_str_bytes::OsStrBytesExt; //! //! # mod env { //! # use std::env; @@ -187,7 +159,7 @@ //! # } //! # //! for file in env::args_os().skip(1) { -//! if !RawOsStr::new(&file).starts_with('-') { +//! if !file.starts_with('-') { //! let string = "Hello, world!"; //! fs::write(&file, string)?; //! assert_eq!(string, fs::read_to_string(file)?); @@ -201,7 +173,6 @@ //! [bstr]: https://crates.io/crates/bstr //! [`ByteSlice::to_os_str`]: https://docs.rs/bstr/0.2.12/bstr/trait.ByteSlice.html#method.to_os_str //! [`ByteVec::into_os_string`]: https://docs.rs/bstr/0.2.12/bstr/trait.ByteVec.html#method.into_os_string -//! [feature]: https://doc.rust-lang.org/unstable-book/library-features/os-str-bytes.html //! [memchr complexity]: RawOsStr#complexity //! [memchr]: https://crates.io/crates/memchr //! [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt @@ -221,10 +192,7 @@ )] #![warn(unused_results)] -#[cfg(any( - feature = "conversions", - all(feature = "raw_os_str", feature = "nightly"), -))] +#[cfg(any(feature = "conversions", feature = "raw_os_str"))] use std::ffi::OsStr; macro_rules! if_conversions { @@ -296,36 +264,15 @@ macro_rules! if_raw_str { }; } -if_raw_str! { - macro_rules! if_not_nightly { - ( $($item:item)+ ) => { - $( - #[cfg(not(feature = "nightly"))] - $item - )+ - }; - } - - macro_rules! if_nightly_return { - ( $nightly_value:block $($not_nightly_token:tt)* ) => { - #[cfg(feature = "nightly")] - return $nightly_value; - #[cfg(not(feature = "nightly"))] - { - $($not_nightly_token)* - } +if_conversions! { + macro_rules! expect_encoded { + ( $result:expr ) => { + $result.expect("invalid raw bytes") }; } } -#[cfg(any(feature = "conversions", feature = "raw_os_str"))] -macro_rules! expect_encoded { - ( $result:expr ) => { - $result.expect("invalid raw bytes") - }; -} - -#[cfg(any(feature = "conversions", feature = "raw_os_str"))] +#[allow(dead_code)] #[cfg_attr( all(target_family = "wasm", target_os = "unknown"), path = "wasm/mod.rs" @@ -337,17 +284,7 @@ macro_rules! expect_encoded { )] mod imp; -#[cfg(any( - all( - feature = "raw_os_str", - any( - feature = "nightly", - all(target_family = "wasm", target_os = "unknown"), - windows, - ), - ), - all(feature = "conversions", windows) -))] +#[cfg(any(feature = "raw_os_str", windows))] mod util; if_raw_str! { @@ -573,141 +510,55 @@ if_conversions! { } if_raw_str! { - if_nightly! { - #[cfg(not(feature = "conversions"))] - trait OsStrBytes: private::Sealed {} - - #[cfg(not(feature = "conversions"))] - impl OsStrBytes for OsStr {} - - /// An extension trait providing methods from [`RawOsStr`]. - #[cfg_attr(not(feature = "conversions"), allow(private_bounds))] - #[cfg_attr( - os_str_bytes_docs_rs, - doc(cfg(all(feature = "nightly", feature = "raw_os_str"))) - )] - pub trait OsStrBytesExt: OsStrBytes { - /// Equivalent to [`str::contains`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert!(os_string.contains("oo")); - /// assert!(!os_string.contains("of")); - /// ``` - #[must_use] - fn contains

(&self, pat: P) -> bool - where - P: Pattern; - - /// Equivalent to [`str::ends_with`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert!(os_string.ends_with("bar")); - /// assert!(!os_string.ends_with("foo")); - /// ``` - #[must_use] - fn ends_with

(&self, pat: P) -> bool - where - P: Pattern; - - if_conversions! { - /// Equivalent to [`str::ends_with`] but accepts this type for - /// the pattern. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert!(os_string.ends_with_os(OsStr::new("bar"))); - /// assert!(!os_string.ends_with_os(OsStr::new("foo"))); - /// ``` - #[cfg_attr( - os_str_bytes_docs_rs, - doc(cfg(feature = "conversions")) - )] - #[must_use] - fn ends_with_os(&self, pat: &Self) -> bool; - } + #[cfg(not(feature = "conversions"))] + trait OsStrBytes: private::Sealed {} - /// Equivalent to [`str::find`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert_eq!(Some(1), os_string.find("o")); - /// assert_eq!(None, os_string.find("of")); - /// ``` - #[must_use] - fn find

(&self, pat: P) -> Option - where - P: Pattern; + #[cfg(not(feature = "conversions"))] + impl OsStrBytes for OsStr {} - /// Equivalent to [`str::rfind`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert_eq!(Some(2), os_string.rfind("o")); - /// assert_eq!(None, os_string.rfind("of")); - /// ``` - #[must_use] - fn rfind

(&self, pat: P) -> Option - where - P: Pattern; + /// An extension trait providing methods from [`RawOsStr`]. + #[cfg_attr(not(feature = "conversions"), allow(private_bounds))] + #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "raw_os_str")))] + pub trait OsStrBytesExt: OsStrBytes { + /// Equivalent to [`str::contains`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert!(os_string.contains("oo")); + /// assert!(!os_string.contains("of")); + /// ``` + #[must_use] + fn contains

(&self, pat: P) -> bool + where + P: Pattern; - /// Equivalent to [`str::rsplit_once`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert_eq!( - /// Some((OsStr::new("fo"), OsStr::new("bar"))), - /// os_string.rsplit_once("o"), - /// ); - /// assert_eq!(None, os_string.rsplit_once("of")); - /// ``` - #[must_use] - fn rsplit_once

(&self, pat: P) -> Option<(&Self, &Self)> - where - P: Pattern; + /// Equivalent to [`str::ends_with`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert!(os_string.ends_with("bar")); + /// assert!(!os_string.ends_with("foo")); + /// ``` + #[must_use] + fn ends_with

(&self, pat: P) -> bool + where + P: Pattern; - /// Equivalent to [`str::split_at`]. - /// - /// # Panics - /// - /// Panics if the index is not a [valid boundary]. + if_conversions! { + /// Equivalent to [`str::ends_with`] but accepts this type for the + /// pattern. /// /// # Examples /// @@ -717,158 +568,141 @@ if_raw_str! { /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); - /// assert_eq!( - /// ((OsStr::new("fo"), OsStr::new("obar"))), - /// os_string.split_at(2), - /// ); + /// assert!(os_string.ends_with_os(OsStr::new("bar"))); + /// assert!(!os_string.ends_with_os(OsStr::new("foo"))); /// ``` - /// - /// [valid boundary]: RawOsStr#indices + #[cfg_attr( + os_str_bytes_docs_rs, + doc(cfg(feature = "conversions")) + )] #[must_use] - #[track_caller] - fn split_at(&self, mid: usize) -> (&Self, &Self); + fn ends_with_os(&self, pat: &Self) -> bool; + } - /// Equivalent to [`str::split_once`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert_eq!( - /// Some((OsStr::new("f"), OsStr::new("obar"))), - /// os_string.split_once("o"), - /// ); - /// assert_eq!(None, os_string.split_once("of")); - /// ``` - #[must_use] - fn split_once

(&self, pat: P) -> Option<(&Self, &Self)> - where - P: Pattern; + /// Equivalent to [`str::find`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert_eq!(Some(1), os_string.find("o")); + /// assert_eq!(None, os_string.find("of")); + /// ``` + #[must_use] + fn find

(&self, pat: P) -> Option + where + P: Pattern; - /// Equivalent to [`str::starts_with`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert!(os_string.starts_with("foo")); - /// assert!(!os_string.starts_with("bar")); - /// ``` - #[must_use] - fn starts_with

(&self, pat: P) -> bool - where - P: Pattern; - - if_conversions! { - /// Equivalent to [`str::starts_with`] but accepts this type - /// for the pattern. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("foobar"); - /// assert!(os_string.starts_with_os(OsStr::new("foo"))); - /// assert!(!os_string.starts_with_os(OsStr::new("bar"))); - /// ``` - #[cfg_attr( - os_str_bytes_docs_rs, - doc(cfg(feature = "conversions")) - )] - #[must_use] - fn starts_with_os(&self, pat: &Self) -> bool; - } + /// Equivalent to [`str::rfind`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert_eq!(Some(2), os_string.rfind("o")); + /// assert_eq!(None, os_string.rfind("of")); + /// ``` + #[must_use] + fn rfind

(&self, pat: P) -> Option + where + P: Pattern; - /// Equivalent to [`str::strip_prefix`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("111foo1bar111"); - /// assert_eq!( - /// Some(OsStr::new("11foo1bar111")), - /// os_string.strip_prefix("1"), - /// ); - /// assert_eq!(None, os_string.strip_prefix("o")); - /// ``` - #[must_use] - fn strip_prefix

(&self, pat: P) -> Option<&Self> - where - P: Pattern; + /// Equivalent to [`str::rsplit_once`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert_eq!( + /// Some((OsStr::new("fo"), OsStr::new("bar"))), + /// os_string.rsplit_once("o"), + /// ); + /// assert_eq!(None, os_string.rsplit_once("of")); + /// ``` + #[must_use] + fn rsplit_once

(&self, pat: P) -> Option<(&Self, &Self)> + where + P: Pattern; - /// Equivalent to [`str::strip_suffix`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("111foo1bar111"); - /// assert_eq!( - /// Some(OsStr::new("111foo1bar11")), - /// os_string.strip_suffix("1"), - /// ); - /// assert_eq!(None, os_string.strip_suffix("o")); - /// ``` - #[must_use] - fn strip_suffix

(&self, pat: P) -> Option<&Self> - where - P: Pattern; + /// Equivalent to [`str::split_at`]. + /// + /// # Panics + /// + /// Panics if the index is not a [valid boundary]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert_eq!( + /// ((OsStr::new("fo"), OsStr::new("obar"))), + /// os_string.split_at(2), + /// ); + /// ``` + /// + /// [valid boundary]: RawOsStr#indices + #[must_use] + #[track_caller] + fn split_at(&self, mid: usize) -> (&Self, &Self); - /// Equivalent to [`str::trim_end_matches`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("111foo1bar111"); - /// assert_eq!("111foo1bar", os_string.trim_end_matches("1")); - /// assert_eq!("111foo1bar111", os_string.trim_end_matches("o")); - /// ``` - #[must_use] - fn trim_end_matches

(&self, pat: P) -> &Self - where - P: Pattern; + /// Equivalent to [`str::split_once`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert_eq!( + /// Some((OsStr::new("f"), OsStr::new("obar"))), + /// os_string.split_once("o"), + /// ); + /// assert_eq!(None, os_string.split_once("of")); + /// ``` + #[must_use] + fn split_once

(&self, pat: P) -> Option<(&Self, &Self)> + where + P: Pattern; - /// Equivalent to [`str::trim_matches`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// use os_str_bytes::OsStrBytesExt; - /// - /// let os_string = OsStr::new("111foo1bar111"); - /// assert_eq!("foo1bar", os_string.trim_matches("1")); - /// assert_eq!("111foo1bar111", os_string.trim_matches("o")); - /// ``` - #[must_use] - fn trim_matches

(&self, pat: P) -> &Self - where - P: Pattern; + /// Equivalent to [`str::starts_with`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("foobar"); + /// assert!(os_string.starts_with("foo")); + /// assert!(!os_string.starts_with("bar")); + /// ``` + #[must_use] + fn starts_with

(&self, pat: P) -> bool + where + P: Pattern; - /// Equivalent to [`str::trim_start_matches`]. + if_conversions! { + /// Equivalent to [`str::starts_with`] but accepts this type + /// for the pattern. /// /// # Examples /// @@ -877,147 +711,244 @@ if_raw_str! { /// /// use os_str_bytes::OsStrBytesExt; /// - /// let os_string = OsStr::new("111foo1bar111"); - /// assert_eq!("foo1bar111", os_string.trim_start_matches("1")); - /// assert_eq!("111foo1bar111", os_string.trim_start_matches("o")); + /// let os_string = OsStr::new("foobar"); + /// assert!(os_string.starts_with_os(OsStr::new("foo"))); + /// assert!(!os_string.starts_with_os(OsStr::new("bar"))); /// ``` + #[cfg_attr( + os_str_bytes_docs_rs, + doc(cfg(feature = "conversions")) + )] #[must_use] - fn trim_start_matches

(&self, pat: P) -> &Self - where - P: Pattern; + fn starts_with_os(&self, pat: &Self) -> bool; } - impl OsStrBytesExt for OsStr { - #[inline] - fn contains

(&self, pat: P) -> bool - where - P: Pattern, - { - RawOsStr::from_os_str(self).contains(pat) - } + /// Equivalent to [`str::strip_prefix`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("111foo1bar111"); + /// assert_eq!( + /// Some(OsStr::new("11foo1bar111")), + /// os_string.strip_prefix("1"), + /// ); + /// assert_eq!(None, os_string.strip_prefix("o")); + /// ``` + #[must_use] + fn strip_prefix

(&self, pat: P) -> Option<&Self> + where + P: Pattern; - #[inline] - fn ends_with

(&self, pat: P) -> bool - where - P: Pattern, - { - RawOsStr::from_os_str(self).ends_with(pat) - } + /// Equivalent to [`str::strip_suffix`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("111foo1bar111"); + /// assert_eq!( + /// Some(OsStr::new("111foo1bar11")), + /// os_string.strip_suffix("1"), + /// ); + /// assert_eq!(None, os_string.strip_suffix("o")); + /// ``` + #[must_use] + fn strip_suffix

(&self, pat: P) -> Option<&Self> + where + P: Pattern; - if_conversions! { - #[inline] - fn ends_with_os(&self, pat: &Self) -> bool { - RawOsStr::from_os_str(self) - .ends_with_os(RawOsStr::from_os_str(pat)) - } - } + /// Equivalent to [`str::trim_end_matches`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("111foo1bar111"); + /// assert_eq!("111foo1bar", os_string.trim_end_matches("1")); + /// assert_eq!("111foo1bar111", os_string.trim_end_matches("o")); + /// ``` + #[must_use] + fn trim_end_matches

(&self, pat: P) -> &Self + where + P: Pattern; - #[inline] - fn find

(&self, pat: P) -> Option - where - P: Pattern, - { - RawOsStr::from_os_str(self).find(pat) - } + /// Equivalent to [`str::trim_matches`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("111foo1bar111"); + /// assert_eq!("foo1bar", os_string.trim_matches("1")); + /// assert_eq!("111foo1bar111", os_string.trim_matches("o")); + /// ``` + #[must_use] + fn trim_matches

(&self, pat: P) -> &Self + where + P: Pattern; - #[inline] - fn rfind

(&self, pat: P) -> Option - where - P: Pattern, - { - RawOsStr::from_os_str(self).rfind(pat) - } + /// Equivalent to [`str::trim_start_matches`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use os_str_bytes::OsStrBytesExt; + /// + /// let os_string = OsStr::new("111foo1bar111"); + /// assert_eq!("foo1bar111", os_string.trim_start_matches("1")); + /// assert_eq!("111foo1bar111", os_string.trim_start_matches("o")); + /// ``` + #[must_use] + fn trim_start_matches

(&self, pat: P) -> &Self + where + P: Pattern; + } - #[inline] - fn rsplit_once

(&self, pat: P) -> Option<(&Self, &Self)> - where - P: Pattern, - { - RawOsStr::from_os_str(self) - .rsplit_once(pat) - .map(|(prefix, suffix)| { - (prefix.as_os_str(), suffix.as_os_str()) - }) - } + impl OsStrBytesExt for OsStr { + #[inline] + fn contains

(&self, pat: P) -> bool + where + P: Pattern, + { + RawOsStr::from_os_str(self).contains(pat) + } - #[inline] - fn split_at(&self, mid: usize) -> (&Self, &Self) { - let (prefix, suffix) = - RawOsStr::from_os_str(self).split_at(mid); - (prefix.as_os_str(), suffix.as_os_str()) - } + #[inline] + fn ends_with

(&self, pat: P) -> bool + where + P: Pattern, + { + RawOsStr::from_os_str(self).ends_with(pat) + } + if_conversions! { #[inline] - fn split_once

(&self, pat: P) -> Option<(&Self, &Self)> - where - P: Pattern, - { + fn ends_with_os(&self, pat: &Self) -> bool { RawOsStr::from_os_str(self) - .split_once(pat) - .map(|(prefix, suffix)| { - (prefix.as_os_str(), suffix.as_os_str()) - }) + .ends_with_os(RawOsStr::from_os_str(pat)) } + } - #[inline] - fn starts_with

(&self, pat: P) -> bool - where - P: Pattern, - { - RawOsStr::from_os_str(self).starts_with(pat) - } + #[inline] + fn find

(&self, pat: P) -> Option + where + P: Pattern, + { + RawOsStr::from_os_str(self).find(pat) + } - if_conversions! { - #[inline] - fn starts_with_os(&self, pat: &Self) -> bool { - RawOsStr::from_os_str(self) - .starts_with_os(RawOsStr::from_os_str(pat)) - } - } + #[inline] + fn rfind

(&self, pat: P) -> Option + where + P: Pattern, + { + RawOsStr::from_os_str(self).rfind(pat) + } - #[inline] - fn strip_prefix

(&self, pat: P) -> Option<&Self> - where - P: Pattern, - { - RawOsStr::from_os_str(self) - .strip_prefix(pat) - .map(RawOsStr::as_os_str) - } + #[inline] + fn rsplit_once

(&self, pat: P) -> Option<(&Self, &Self)> + where + P: Pattern, + { + RawOsStr::from_os_str(self) + .rsplit_once(pat) + .map(|(prefix, suffix)| { + (prefix.as_os_str(), suffix.as_os_str()) + }) + } + + #[inline] + fn split_at(&self, mid: usize) -> (&Self, &Self) { + let (prefix, suffix) = RawOsStr::from_os_str(self).split_at(mid); + (prefix.as_os_str(), suffix.as_os_str()) + } + + #[inline] + fn split_once

(&self, pat: P) -> Option<(&Self, &Self)> + where + P: Pattern, + { + RawOsStr::from_os_str(self) + .split_once(pat) + .map(|(prefix, suffix)| { + (prefix.as_os_str(), suffix.as_os_str()) + }) + } + + #[inline] + fn starts_with

(&self, pat: P) -> bool + where + P: Pattern, + { + RawOsStr::from_os_str(self).starts_with(pat) + } + if_conversions! { #[inline] - fn strip_suffix

(&self, pat: P) -> Option<&Self> - where - P: Pattern, - { + fn starts_with_os(&self, pat: &Self) -> bool { RawOsStr::from_os_str(self) - .strip_suffix(pat) - .map(RawOsStr::as_os_str) + .starts_with_os(RawOsStr::from_os_str(pat)) } + } - #[inline] - fn trim_end_matches

(&self, pat: P) -> &Self - where - P: Pattern, - { - RawOsStr::from_os_str(self).trim_end_matches(pat).as_os_str() - } + #[inline] + fn strip_prefix

(&self, pat: P) -> Option<&Self> + where + P: Pattern, + { + RawOsStr::from_os_str(self) + .strip_prefix(pat) + .map(RawOsStr::as_os_str) + } - #[inline] - fn trim_matches

(&self, pat: P) -> &Self - where - P: Pattern, - { - RawOsStr::from_os_str(self).trim_matches(pat).as_os_str() - } + #[inline] + fn strip_suffix

(&self, pat: P) -> Option<&Self> + where + P: Pattern, + { + RawOsStr::from_os_str(self) + .strip_suffix(pat) + .map(RawOsStr::as_os_str) + } - #[inline] - fn trim_start_matches

(&self, pat: P) -> &Self - where - P: Pattern, - { - RawOsStr::from_os_str(self).trim_start_matches(pat).as_os_str() - } + #[inline] + fn trim_end_matches

(&self, pat: P) -> &Self + where + P: Pattern, + { + RawOsStr::from_os_str(self).trim_end_matches(pat).as_os_str() + } + + #[inline] + fn trim_matches

(&self, pat: P) -> &Self + where + P: Pattern, + { + RawOsStr::from_os_str(self).trim_matches(pat).as_os_str() + } + + #[inline] + fn trim_start_matches

(&self, pat: P) -> &Self + where + P: Pattern, + { + RawOsStr::from_os_str(self).trim_start_matches(pat).as_os_str() } } } diff --git a/src/raw_str.rs b/src/raw_str.rs index d694f46..6086fdc 100644 --- a/src/raw_str.rs +++ b/src/raw_str.rs @@ -1,5 +1,3 @@ -#![cfg_attr(feature = "nightly", allow(unreachable_code))] - use std::borrow::Borrow; use std::borrow::Cow; use std::borrow::ToOwned; @@ -26,11 +24,11 @@ use memchr::memmem::find; #[cfg(feature = "memchr")] use memchr::memmem::rfind; -use super::imp; use super::imp::raw; use super::iter::RawSplit; use super::pattern::Encoded as EncodedPattern; use super::private; +use super::util; use super::Pattern; if_checked_conversions! { @@ -38,8 +36,8 @@ if_checked_conversions! { use super::Result; } -if_nightly! { - use super::util; +if_conversions! { + use super::imp; } #[cfg(not(feature = "memchr"))] @@ -121,17 +119,7 @@ impl RawOsStr { unsafe { mem::transmute(string) } } - /// Converts a platform-native string into a representation that can be - /// more easily manipulated. - /// - /// This method performs the necessary conversion immediately, so it can be - /// expensive to call. It is recommended to continue using the returned - /// instance as long as possible (instead of the original [`OsStr`]), to - /// avoid repeated conversions. - /// - /// # Nightly Notes - /// - /// This method is deprecated. Use [`from_os_str`] instead. + /// Wraps a platform-native string, without copying or encoding conversion. /// /// # Examples /// @@ -142,51 +130,14 @@ impl RawOsStr { /// use os_str_bytes::RawOsStr; /// /// let os_string = env::current_exe()?.into_os_string(); - /// println!("{:?}", RawOsStr::new(&os_string)); + /// println!("{:?}", RawOsStr::from_os_str(&os_string)); /// # /// # Ok::<_, io::Error>(()) /// ``` - /// - /// [`from_os_str`]: Self::from_os_str - #[cfg_attr( - all(not(os_str_bytes_docs_rs), feature = "nightly"), - deprecated(since = "6.6.0", note = "use `from_os_str` instead") - )] #[inline] #[must_use] - pub fn new(string: &OsStr) -> Cow<'_, Self> { - if_nightly_return! {{ - Cow::Borrowed(Self::from_os_str(string)) - }} - match imp::os_str_to_bytes(string) { - Cow::Borrowed(string) => Cow::Borrowed(Self::from_inner(string)), - Cow::Owned(string) => Cow::Owned(RawOsString(string)), - } - } - - if_nightly! { - /// Wraps a platform-native string, without copying or encoding - /// conversion. - /// - /// # Examples - /// - /// ``` - /// use std::env; - /// # use std::io; - /// - /// use os_str_bytes::RawOsStr; - /// - /// let os_string = env::current_exe()?.into_os_string(); - /// println!("{:?}", RawOsStr::from_os_str(&os_string)); - /// # - /// # Ok::<_, io::Error>(()) - /// ``` - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - #[inline] - #[must_use] - pub fn from_os_str(string: &OsStr) -> &Self { - Self::from_inner(string.as_encoded_bytes()) - } + pub fn from_os_str(string: &OsStr) -> &Self { + Self::from_inner(string.as_encoded_bytes()) } /// Wraps a string, without copying or encoding conversion. @@ -212,48 +163,39 @@ impl RawOsStr { Self::from_inner(string.as_bytes()) } - if_nightly! { - /// Equivalent to [`OsStr::from_encoded_bytes_unchecked`]. - /// - /// # Examples - /// - /// ``` - /// use std::env; - /// # use std::io; - /// - /// use os_str_bytes::RawOsStr; - /// - /// let os_string = env::current_exe()?.into_os_string(); - /// let raw = RawOsStr::from_os_str(&os_string); - /// let raw_bytes = raw.as_encoded_bytes(); - /// assert_eq!(raw, unsafe { - /// RawOsStr::from_encoded_bytes_unchecked(raw_bytes) - /// }); - /// # - /// # Ok::<_, io::Error>(()) - /// ``` - /// - /// [unspecified encoding]: super#encoding - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - #[inline] - #[must_use] - pub unsafe fn from_encoded_bytes_unchecked(string: &[u8]) -> &Self { - Self::from_inner(string) - } + /// Equivalent to [`OsStr::from_encoded_bytes_unchecked`]. + /// + /// # Examples + /// + /// ``` + /// use std::env; + /// # use std::io; + /// + /// use os_str_bytes::RawOsStr; + /// + /// let os_string = env::current_exe()?.into_os_string(); + /// let raw = RawOsStr::from_os_str(&os_string); + /// let raw_bytes = raw.as_encoded_bytes(); + /// assert_eq!(raw, unsafe { + /// RawOsStr::from_encoded_bytes_unchecked(raw_bytes) + /// }); + /// # + /// # Ok::<_, io::Error>(()) + /// ``` + /// + /// [unspecified encoding]: super#encoding + #[allow(clippy::missing_safety_doc)] + #[inline] + #[must_use] + pub unsafe fn from_encoded_bytes_unchecked(string: &[u8]) -> &Self { + Self::from_inner(string) } if_conversions! { fn cow_from_raw_bytes_checked( string: &[u8], ) -> imp::Result> { - if_nightly_return! { - { - imp::os_str_from_bytes(string) - .map(RawOsStrCow::from_os_str) - } - raw::validate_bytes(string) - .map(|()| Cow::Borrowed(Self::from_inner(string))) - } + imp::os_str_from_bytes(string).map(RawOsStrCow::from_os_str) } } @@ -276,9 +218,9 @@ impl RawOsStr { /// use os_str_bytes::RawOsStr; /// /// let os_string = env::current_exe()?.into_os_string(); - /// let raw = RawOsStr::new(&os_string); + /// let raw = RawOsStr::from_os_str(&os_string); /// let raw_bytes = raw.to_raw_bytes(); - /// assert_eq!(raw, RawOsStr::assert_cow_from_raw_bytes(&raw_bytes)); + /// assert_eq!(raw, &*RawOsStr::assert_cow_from_raw_bytes(&raw_bytes)); /// # /// # Ok::<_, io::Error>(()) /// ``` @@ -312,11 +254,11 @@ impl RawOsStr { /// use os_str_bytes::RawOsStr; /// /// let os_string = env::current_exe()?.into_os_string(); - /// let raw = RawOsStr::new(&os_string); + /// let raw = RawOsStr::from_os_str(&os_string); /// let raw_bytes = raw.to_raw_bytes(); /// assert_eq!( - /// Ok(&raw), - /// RawOsStr::cow_from_raw_bytes(&raw_bytes).as_ref(), + /// Ok(raw), + /// RawOsStr::cow_from_raw_bytes(&raw_bytes).as_deref(), /// ); /// # /// # Ok::<_, io::Error>(()) @@ -333,65 +275,53 @@ impl RawOsStr { } } - if_nightly! { - /// Equivalent to [`OsStr::as_encoded_bytes`]. - /// - /// The returned string will not use the [unspecified encoding]. It can - /// only be passed to methods accepting the encoding from the standard - /// library, such as [`from_encoded_bytes_unchecked`]. - /// - /// # Examples - /// - /// ``` - /// use os_str_bytes::RawOsStr; - /// - /// let string = "foobar"; - /// let raw = RawOsStr::from_str(string); - /// assert_eq!(string.as_bytes(), raw.as_encoded_bytes()); - /// ``` - /// - /// [`from_encoded_bytes_unchecked`]: Self::from_encoded_bytes_unchecked - /// [unspecified encoding]: super#encoding - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - #[inline] - #[must_use] - pub fn as_encoded_bytes(&self) -> &[u8] { - &self.0 - } - } - - if_not_nightly! { - pub(super) fn as_raw_bytes(&self) -> &[u8] { - &self.0 - } + /// Equivalent to [`OsStr::as_encoded_bytes`]. + /// + /// The returned string will not use the [unspecified encoding]. It can + /// only be passed to methods accepting the encoding from the standard + /// library, such as [`from_encoded_bytes_unchecked`]. + /// + /// # Examples + /// + /// ``` + /// use os_str_bytes::RawOsStr; + /// + /// let string = "foobar"; + /// let raw = RawOsStr::from_str(string); + /// assert_eq!(string.as_bytes(), raw.as_encoded_bytes()); + /// ``` + /// + /// [`from_encoded_bytes_unchecked`]: Self::from_encoded_bytes_unchecked + /// [unspecified encoding]: super#encoding + #[inline] + #[must_use] + pub fn as_encoded_bytes(&self) -> &[u8] { + &self.0 } - if_nightly! { - /// Converts this representation back to a platform-native string, - /// without copying or encoding conversion. - /// - /// # Examples - /// - /// ``` - /// use std::env; - /// # use std::io; - /// - /// use os_str_bytes::RawOsStr; - /// - /// let os_string = env::current_exe()?.into_os_string(); - /// let raw = RawOsStr::from_os_str(&os_string); - /// assert_eq!(os_string, raw.as_os_str()); - /// # - /// # Ok::<_, io::Error>(()) - /// ``` - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - #[inline] - #[must_use] - pub fn as_os_str(&self) -> &OsStr { - // SAFETY: This wrapper prevents violating the invariants of the - // encoding used by the standard library. - unsafe { OsStr::from_encoded_bytes_unchecked(&self.0) } - } + /// Converts this representation back to a platform-native string, without + /// copying or encoding conversion. + /// + /// # Examples + /// + /// ``` + /// use std::env; + /// # use std::io; + /// + /// use os_str_bytes::RawOsStr; + /// + /// let os_string = env::current_exe()?.into_os_string(); + /// let raw = RawOsStr::from_os_str(&os_string); + /// assert_eq!(os_string, raw.as_os_str()); + /// # + /// # Ok::<_, io::Error>(()) + /// ``` + #[inline] + #[must_use] + pub fn as_os_str(&self) -> &OsStr { + // SAFETY: This wrapper prevents violating the invariants of the + // encoding used by the standard library. + unsafe { OsStr::from_encoded_bytes_unchecked(&self.0) } } /// Equivalent to [`str::contains`]. @@ -567,83 +497,51 @@ impl RawOsStr { self.rsplit_once_raw(&pat.__encode()) } + #[allow(clippy::items_after_statements)] fn is_boundary(&self, index: usize) -> bool { debug_assert!(index < self.0.len()); - if_nightly_return! { - { - const MAX_LENGTH: usize = 4; + const MAX_LENGTH: usize = 4; - if index == 0 { - return true; - } - let byte = self.0[index]; - if byte.is_ascii() { - return true; - } + if index == 0 { + return true; + } + let byte = self.0[index]; + if byte.is_ascii() { + return true; + } - if !util::is_continuation(byte) { - let bytes = &self.0[index..]; - let valid = - str::from_utf8(&bytes[..bytes.len().min(MAX_LENGTH)]) - .err() - .map(|x| x.valid_up_to() != 0) - .unwrap_or(true); - if valid { - return true; - } - } - let mut start = index; - for _ in 0..MAX_LENGTH { - if let Some(index) = start.checked_sub(1) { - start = index; - } else { - return false; - } - if !util::is_continuation(self.0[start]) { - break; - } - } - str::from_utf8(&self.0[start..index]).is_ok() + if !util::is_continuation(byte) { + let bytes = &self.0[index..]; + let valid = str::from_utf8(&bytes[..bytes.len().min(MAX_LENGTH)]) + .err() + .map(|x| x.valid_up_to() != 0) + .unwrap_or(true); + if valid { + return true; } - !raw::is_continuation(self.0[index]) } - } - - #[cfg_attr(feature = "nightly", allow(clippy::diverging_sub_expression))] - #[cold] - #[inline(never)] - #[track_caller] - fn index_boundary_error(&self, index: usize) -> ! { - debug_assert!(!self.is_boundary(index)); - - if_nightly_return! { - { - panic!("byte index {} is not a valid boundary", index); + let mut start = index; + for _ in 0..MAX_LENGTH { + if let Some(index) = start.checked_sub(1) { + start = index; + } else { + return false; + } + if !util::is_continuation(self.0[start]) { + break; } - let start = expect_encoded!(self.0[..index] - .iter() - .rposition(|&x| !raw::is_continuation(x))); - let mut end = index + 1; - end += self.0[end..] - .iter() - .take_while(|&&x| raw::is_continuation(x)) - .count(); - - let code_point = raw::decode_code_point(&self.0[start..end]); - panic!( - "byte index {} is not a valid boundary; it is inside U+{:04X} \ - (bytes {}..{})", - index, code_point, start, end, - ); } + str::from_utf8(&self.0[start..index]).is_ok() } #[track_caller] fn check_bound(&self, index: usize) { - if index < self.0.len() && !self.is_boundary(index) { - self.index_boundary_error(index); - } + assert!( + index >= self.0.len() || self.is_boundary(index), + "byte index {} is not a valid boundary", + index, + ); } /// Equivalent to [`str::split`], but empty patterns are not accepted. @@ -825,13 +723,6 @@ impl RawOsStr { self.0.strip_suffix(pat).map(Self::from_inner) } - fn to_os_str(&self) -> Cow<'_, OsStr> { - if_nightly_return! {{ - Cow::Borrowed(self.as_os_str()) - }} - expect_encoded!(imp::os_str_from_bytes(&self.0)) - } - if_conversions! { /// Converts and returns the byte string stored by this container. /// @@ -852,10 +743,7 @@ impl RawOsStr { #[inline] #[must_use] pub fn to_raw_bytes(&self) -> Cow<'_, [u8]> { - if_nightly_return! {{ - imp::os_str_to_bytes(self.as_os_str()) - }} - Cow::Borrowed(&self.0) + imp::os_str_to_bytes(self.as_os_str()) } } @@ -894,7 +782,7 @@ impl RawOsStr { /// use os_str_bytes::RawOsStr; /// /// let os_string = env::current_exe()?.into_os_string(); - /// let raw = RawOsStr::new(&os_string); + /// let raw = RawOsStr::from_os_str(&os_string); /// println!("{}", raw.to_str_lossy()); /// # /// # Ok::<_, io::Error>(()) @@ -1005,31 +893,24 @@ impl AsRef for RawOsStr { } } -if_nightly! { - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - impl AsRef for RawOsStr { - #[inline] - fn as_ref(&self) -> &OsStr { - self.as_os_str() - } +impl AsRef for RawOsStr { + #[inline] + fn as_ref(&self) -> &OsStr { + self.as_os_str() } +} - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - impl AsRef for OsStr { - #[inline] - fn as_ref(&self) -> &RawOsStr { - RawOsStr::from_os_str(self) - } +impl AsRef for OsStr { + #[inline] + fn as_ref(&self) -> &RawOsStr { + RawOsStr::from_os_str(self) } } -if_nightly! { - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - impl AsRef for OsString { - #[inline] - fn as_ref(&self) -> &RawOsStr { - (**self).as_ref() - } +impl AsRef for OsString { + #[inline] + fn as_ref(&self) -> &RawOsStr { + (**self).as_ref() } } @@ -1115,6 +996,7 @@ pub trait RawOsStrCow<'a>: private::Sealed { /// # Examples /// /// ``` + /// use std::borrow::Cow; /// use std::env; /// # use std::io; /// @@ -1122,7 +1004,7 @@ pub trait RawOsStrCow<'a>: private::Sealed { /// use os_str_bytes::RawOsStrCow; /// /// let os_string = env::current_exe()?.into_os_string(); - /// let raw = RawOsStr::new(&os_string); + /// let raw = Cow::Borrowed(RawOsStr::from_os_str(&os_string)); /// assert_eq!(os_string, raw.into_os_str()); /// # /// # Ok::<_, io::Error>(()) @@ -1132,11 +1014,12 @@ pub trait RawOsStrCow<'a>: private::Sealed { } impl<'a> RawOsStrCow<'a> for Cow<'a, RawOsStr> { - #[cfg_attr(feature = "nightly", allow(deprecated))] #[inline] fn from_os_str(string: Cow<'a, OsStr>) -> Self { match string { - Cow::Borrowed(string) => RawOsStr::new(string), + Cow::Borrowed(string) => { + Cow::Borrowed(RawOsStr::from_os_str(string)) + } Cow::Owned(string) => Cow::Owned(RawOsString::new(string)), } } @@ -1144,7 +1027,7 @@ impl<'a> RawOsStrCow<'a> for Cow<'a, RawOsStr> { #[inline] fn into_os_str(self) -> Cow<'a, OsStr> { match self { - Cow::Borrowed(string) => string.to_os_str(), + Cow::Borrowed(string) => Cow::Borrowed(string.as_os_str()), Cow::Owned(string) => Cow::Owned(string.into_os_string()), } } @@ -1183,10 +1066,7 @@ impl RawOsString { #[inline] #[must_use] pub fn new(string: OsString) -> Self { - if_nightly_return! {{ - Self(string.into_encoded_bytes()) - }} - Self(imp::os_string_into_vec(string)) + Self(string.into_encoded_bytes()) } /// Wraps a string, without copying or encoding conversion. @@ -1211,42 +1091,35 @@ impl RawOsString { Self(string.into_bytes()) } - if_nightly! { - /// Equivalent to [`OsString::from_encoded_bytes_unchecked`]. - /// - /// # Examples - /// - /// ``` - /// use std::env; - /// # use std::io; - /// - /// use os_str_bytes::RawOsString; - /// - /// let os_string = env::current_exe()?.into_os_string(); - /// let raw = RawOsString::new(os_string); - /// let raw_bytes = raw.clone().into_encoded_vec(); - /// assert_eq!(raw, unsafe { - /// RawOsString::from_encoded_vec_unchecked(raw_bytes) - /// }); - /// # - /// # Ok::<_, io::Error>(()) - /// ``` - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - #[inline] - #[must_use] - pub unsafe fn from_encoded_vec_unchecked(string: Vec) -> Self { - Self(string) - } + /// Equivalent to [`OsString::from_encoded_bytes_unchecked`]. + /// + /// # Examples + /// + /// ``` + /// use std::env; + /// # use std::io; + /// + /// use os_str_bytes::RawOsString; + /// + /// let os_string = env::current_exe()?.into_os_string(); + /// let raw = RawOsString::new(os_string); + /// let raw_bytes = raw.clone().into_encoded_vec(); + /// assert_eq!(raw, unsafe { + /// RawOsString::from_encoded_vec_unchecked(raw_bytes) + /// }); + /// # + /// # Ok::<_, io::Error>(()) + /// ``` + #[allow(clippy::missing_safety_doc)] + #[inline] + #[must_use] + pub unsafe fn from_encoded_vec_unchecked(string: Vec) -> Self { + Self(string) } if_conversions! { fn from_raw_vec_checked(string: Vec) -> imp::Result { - if_nightly_return! { - { - imp::os_string_from_vec(string).map(Self::new) - } - raw::validate_bytes(&string).map(|()| Self(string)) - } + imp::os_string_from_vec(string).map(Self::new) } } @@ -1360,31 +1233,28 @@ impl RawOsString { self.0.into_boxed_slice().transmute_box() } - if_nightly! { - /// Equivalent to [`OsString::into_encoded_bytes`]. - /// - /// The returned string will not use the [unspecified encoding]. It can - /// only be passed to methods accepting the encoding from the standard - /// library, such as [`from_encoded_vec_unchecked`]. - /// - /// # Examples - /// - /// ``` - /// use os_str_bytes::RawOsString; - /// - /// let string = "foobar".to_owned(); - /// let raw = RawOsString::from_string(string.clone()); - /// assert_eq!(string.into_bytes(), raw.into_encoded_vec()); - /// ``` - /// - /// [`from_encoded_vec_unchecked`]: Self::from_encoded_vec_unchecked - /// [unspecified encoding]: super#encoding - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - #[inline] - #[must_use] - pub fn into_encoded_vec(self) -> Vec { - self.0 - } + /// Equivalent to [`OsString::into_encoded_bytes`]. + /// + /// The returned string will not use the [unspecified encoding]. It can + /// only be passed to methods accepting the encoding from the standard + /// library, such as [`from_encoded_vec_unchecked`]. + /// + /// # Examples + /// + /// ``` + /// use os_str_bytes::RawOsString; + /// + /// let string = "foobar".to_owned(); + /// let raw = RawOsString::from_string(string.clone()); + /// assert_eq!(string.into_bytes(), raw.into_encoded_vec()); + /// ``` + /// + /// [`from_encoded_vec_unchecked`]: Self::from_encoded_vec_unchecked + /// [unspecified encoding]: super#encoding + #[inline] + #[must_use] + pub fn into_encoded_vec(self) -> Vec { + self.0 } /// Converts this representation back to a platform-native string. @@ -1410,12 +1280,9 @@ impl RawOsString { #[inline] #[must_use] pub fn into_os_string(self) -> OsString { - if_nightly_return! {{ - // SAFETY: This wrapper prevents violating the invariants of the - // encoding used by the standard library. - unsafe { OsString::from_encoded_bytes_unchecked(self.0) } - }} - expect_encoded!(imp::os_string_from_vec(self.0)) + // SAFETY: This wrapper prevents violating the invariants of the + // encoding used by the standard library. + unsafe { OsString::from_encoded_bytes_unchecked(self.0) } } if_conversions! { @@ -1438,10 +1305,7 @@ impl RawOsString { #[inline] #[must_use] pub fn into_raw_vec(self) -> Vec { - if_nightly_return! {{ - imp::os_string_into_vec(self.into_os_string()) - }} - self.0 + imp::os_string_into_vec(self.into_os_string()) } } @@ -1530,13 +1394,10 @@ impl RawOsString { } } -if_nightly! { - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - impl AsRef for RawOsString { - #[inline] - fn as_ref(&self) -> &OsStr { - (**self).as_ref() - } +impl AsRef for RawOsString { + #[inline] + fn as_ref(&self) -> &OsStr { + (**self).as_ref() } } @@ -1584,21 +1445,17 @@ impl From for Cow<'_, RawOsStr> { } } -if_nightly! { - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - impl From for RawOsString { - #[inline] - fn from(value: OsString) -> Self { - Self::new(value) - } +impl From for RawOsString { + #[inline] + fn from(value: OsString) -> Self { + Self::new(value) } +} - #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "nightly")))] - impl From for OsString { - #[inline] - fn from(value: RawOsString) -> Self { - value.into_os_string() - } +impl From for OsString { + #[inline] + fn from(value: RawOsString) -> Self { + value.into_os_string() } } @@ -1722,35 +1579,21 @@ macro_rules! r#impl { } }; } +r#impl!(RawOsStr, OsStr); +r#impl!(RawOsStr, OsString); r#impl!(RawOsStr, RawOsString); r#impl!(RawOsStr, str); r#impl!(RawOsStr, String); +r#impl!(&RawOsStr, OsString); r#impl!(&RawOsStr, RawOsString); r#impl!(&RawOsStr, String); +r#impl!(RawOsString, OsStr); +r#impl!(RawOsString, &OsStr); +r#impl!(RawOsString, OsString); r#impl!(RawOsString, str); r#impl!(RawOsString, &str); r#impl!(RawOsString, String); -if_nightly! { - macro_rules! impl_nightly { - ( $type:ty , $other_type:ty ) => { - r#impl! { - #[cfg_attr( - os_str_bytes_docs_rs, - doc(cfg(feature = "nightly")) - )] - $type, $other_type - } - }; - } - impl_nightly!(RawOsStr, OsStr); - impl_nightly!(RawOsStr, OsString); - impl_nightly!(&RawOsStr, OsString); - impl_nightly!(RawOsString, OsStr); - impl_nightly!(RawOsString, &OsStr); - impl_nightly!(RawOsString, OsString); -} - #[cfg(feature = "print_bytes")] #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "print_bytes")))] mod print_bytes { diff --git a/src/wasm/raw.rs b/src/wasm/raw.rs index 80e0256..3492dfb 100644 --- a/src/wasm/raw.rs +++ b/src/wasm/raw.rs @@ -3,19 +3,6 @@ use std::fmt::Formatter; use crate::RawOsStr; -if_not_nightly! { - use std::str; - - if_conversions! { - use super::Result; - } - - if_raw_str! { - pub(crate) use crate::util::is_continuation; - } -} - -#[allow(dead_code)] #[path = "../common/raw.rs"] mod common_raw; #[cfg(feature = "uniquote")] @@ -26,26 +13,6 @@ if_conversions! { pub(crate) use common_raw::starts_with; } -if_not_nightly! { - if_conversions! { - pub(crate) fn validate_bytes(string: &[u8]) -> Result<()> { - super::from_bytes(string).map(drop) - } - } -} - -if_not_nightly! { - pub(crate) fn decode_code_point(string: &[u8]) -> u32 { - let string = expect_encoded!(str::from_utf8(string)); - let mut chars = string.chars(); - let ch = chars - .next() - .expect("cannot parse code point from empty string"); - assert_eq!(None, chars.next(), "multiple code points found"); - ch.into() - } -} - pub(crate) fn debug(string: &RawOsStr, _: &mut Formatter<'_>) -> fmt::Result { assert!(string.is_empty()); Ok(()) diff --git a/src/windows/raw.rs b/src/windows/raw.rs index 8f54ca0..59f4732 100644 --- a/src/windows/raw.rs +++ b/src/windows/raw.rs @@ -1,5 +1,6 @@ use std::fmt; use std::fmt::Formatter; +use std::os::windows::ffi::OsStrExt; use crate::RawOsStr; @@ -8,50 +9,10 @@ if_conversions! { pub(crate) use super::wtf8::starts_with; } -if_nightly! { - use std::os::windows::ffi::OsStrExt; -} - -if_not_nightly! { - pub(crate) use crate::util::is_continuation; - - use super::wtf8; - use super::wtf8::CodePoints; - - if_conversions! { - use super::Result; - } -} - -if_not_nightly! { - if_conversions! { - pub(crate) fn validate_bytes(string: &[u8]) -> Result<()> { - wtf8::encode_wide(string).try_for_each(|x| x.map(drop)) - } - } -} - -#[cfg_attr(not(feature = "nightly"), allow(deprecated))] pub(crate) fn encode_wide( string: &RawOsStr, ) -> impl '_ + Iterator { - if_nightly_return! { - { - string.as_os_str().encode_wide() - } - wtf8::encode_wide(string.as_raw_bytes()).map(|x| expect_encoded!(x)) - } -} - -if_not_nightly! { - pub(crate) fn decode_code_point(string: &[u8]) -> u32 { - let mut code_points = CodePoints::new(string.iter().copied()); - let code_point = expect_encoded!(code_points - .next() - .expect("cannot parse code point from empty string")); - assert_eq!(None, code_points.next(), "multiple code points found"); - code_point - } + string.as_os_str().encode_wide() } pub(crate) fn debug(string: &RawOsStr, f: &mut Formatter<'_>) -> fmt::Result { diff --git a/src/windows/wtf8/string.rs b/src/windows/wtf8/string.rs index b3523a2..fa3882a 100644 --- a/src/windows/wtf8/string.rs +++ b/src/windows/wtf8/string.rs @@ -3,9 +3,7 @@ use crate::util; const SURROGATE_LENGTH: usize = 3; pub(crate) fn ends_with(string: &[u8], mut suffix: &[u8]) -> bool { - let index = if let Some(index) = string.len().checked_sub(suffix.len()) { - index - } else { + let Some(index) = string.len().checked_sub(suffix.len()) else { return false; }; if let Some(&byte) = string.get(index) { @@ -38,11 +36,8 @@ pub(crate) fn ends_with(string: &[u8], mut suffix: &[u8]) -> bool { pub(crate) fn starts_with(string: &[u8], mut prefix: &[u8]) -> bool { if let Some(&byte) = string.get(prefix.len()) { if util::is_continuation(byte) { - let index = if let Some(index) = - prefix.len().checked_sub(SURROGATE_LENGTH) - { - index - } else { + let Some(index) = prefix.len().checked_sub(SURROGATE_LENGTH) + else { return false; }; let (substring, surrogate) = prefix.split_at(index); diff --git a/tests/raw_index.rs b/tests/raw_index.rs index bc83e8d..498c890 100644 --- a/tests/raw_index.rs +++ b/tests/raw_index.rs @@ -13,8 +13,6 @@ if_conversions! { use raw_common::RAW_WTF8_STRING; } -const EXPECT_PANIC: bool = !cfg!(unix) || cfg!(feature = "nightly"); - if_conversions! { #[test] fn test_valid() { @@ -35,40 +33,27 @@ if_conversions! { } macro_rules! test { - ( $name:ident , $index:literal , $code_point:expr ) => { + ( $name:ident , $index:literal ) => { // https://github.com/rust-lang/rust/issues/88430 #[test] fn $name() { - let index_fn = || RAW_WTF8_STRING.index($index..); - if !EXPECT_PANIC { - let _ = index_fn(); - return; - } - - let error = panic::catch_unwind(index_fn) - .expect_err("test did not panic as expected"); + let error = + panic::catch_unwind(|| RAW_WTF8_STRING.index($index..)) + .expect_err("test did not panic as expected"); let error: &String = error.downcast_ref().expect("incorrect panic message type"); - let suffix = if cfg!(feature = "nightly") { - "" - } else { - concat!("; it is inside ", $code_point) - }; assert_eq!( - &format!( - "byte index {} is not a valid boundary{}", - $index, suffix, - ), + &format!("byte index {} is not a valid boundary", $index), error, ); } }; } - test!(test_4, 4, "U+D83D (bytes 3..6)"); - test!(test_5, 5, "U+D83D (bytes 3..6)"); - test!(test_7, 7, "U+1F4A9 (bytes 6..10)"); - test!(test_8, 8, "U+1F4A9 (bytes 6..10)"); - test!(test_9, 9, "U+1F4A9 (bytes 6..10)"); + test!(test_4, 4); + test!(test_5, 5); + test!(test_7, 7); + test!(test_8, 8); + test!(test_9, 9); } #[test] @@ -78,7 +63,7 @@ fn test_panics() { where F: FnOnce() -> R + UnwindSafe, { - assert_eq!(EXPECT_PANIC, panic::catch_unwind(f).is_err()); + assert!(panic::catch_unwind(f).is_err()); } let string = RawOsStr::from_str("\u{F6}"); diff --git a/tests/raw_integration.rs b/tests/raw_integration.rs index e2ca8e0..04b2397 100644 --- a/tests/raw_integration.rs +++ b/tests/raw_integration.rs @@ -10,7 +10,6 @@ if_conversions! { } if_conversions! { - #[cfg_attr(feature = "nightly", allow(deprecated))] #[test] fn test_ends_with() { #[track_caller] @@ -39,7 +38,6 @@ if_conversions! { test(false, b"\xED\xB2\xA9aar"); } - #[cfg_attr(feature = "nightly", allow(deprecated))] #[test] fn test_empty_ends_with() { #[track_caller] @@ -56,7 +54,6 @@ if_conversions! { test(false, "ar"); } - #[cfg_attr(feature = "nightly", allow(deprecated))] #[test] fn test_starts_with() { #[track_caller] @@ -85,7 +82,6 @@ if_conversions! { test(false, b"fof\xED\xA0\xBD\xED\xA0\xBD"); } - #[cfg_attr(feature = "nightly", allow(deprecated))] #[test] fn test_empty_starts_with() { #[track_caller] diff --git a/tests/raw_random.rs b/tests/raw_random.rs index e8ac192..0ac5c21 100644 --- a/tests/raw_random.rs +++ b/tests/raw_random.rs @@ -13,7 +13,6 @@ if_conversions! { } if_conversions! { - #[cfg_attr(feature = "nightly", allow(deprecated))] #[test] fn test_complex() { macro_rules! test { @@ -29,7 +28,7 @@ if_conversions! { for _ in 0..ITERATIONS { let mut string = random_common::fastrand_os_string(SMALL_LENGTH); - let prefix = RawOsStr::new(&string).into_owned(); + let prefix = RawOsStr::from_os_str(&string).to_owned(); let suffix = random_common::fastrand_os_string(SMALL_LENGTH); string.push(&suffix);