From 4134cc3736a8be2aff791d1a2f16bf238cc20e41 Mon Sep 17 00:00:00 2001 From: Nicholas Berlette Date: Tue, 23 Dec 2025 15:14:34 -0800 Subject: [PATCH 1/6] feat: add FromStr impl to CowStr --- src/cow_str.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cow_str.rs b/src/cow_str.rs index 8b5807e..3f8245c 100644 --- a/src/cow_str.rs +++ b/src/cow_str.rs @@ -17,6 +17,7 @@ use core::mem::transmute_copy; use core::ops::Deref; use core::ops::DerefMut; use core::str; +use core::str::FromStr; use crate::inline_str::*; @@ -165,6 +166,18 @@ impl<'i> CowStr<'i> { } } +impl<'i> FromStr for CowStr<'i> { + type Err = (); + + #[inline(always)] + fn from_str(s: &str) -> Result { + match InlineStr::try_from(s) { + Ok(inline) => Ok(CowStr::Inlined(inline)), + Err(_) => Ok(CowStr::Borrowed(s)), + } + } +} + impl<'i> Display for CowStr<'i> { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From e965ea913a4beb7f891bcc213e979f913881d3e3 Mon Sep 17 00:00:00 2001 From: Nicholas Berlette Date: Tue, 23 Dec 2025 15:20:42 -0800 Subject: [PATCH 2/6] fix: update version typo in deprecation --- src/cow_str.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cow_str.rs b/src/cow_str.rs index 3f8245c..a958354 100644 --- a/src/cow_str.rs +++ b/src/cow_str.rs @@ -144,9 +144,7 @@ impl<'i> CowStr<'i> { self.len() == 0 } - /// Converts the `CowStr` into an owned `String`, cloning the data if - /// necessary. - #[deprecated(since = "0.4.0", note = "use `into_string` instead")] + #[deprecated(since = "0.2.0", note = "use `into_string` instead")] #[inline(always)] pub fn into_owned(self) -> String { match self { @@ -156,6 +154,8 @@ impl<'i> CowStr<'i> { } } + /// Converts the `CowStr` into an owned `String`, cloning the data if + /// necessary. #[inline(always)] pub fn into_string(self) -> String { match self { From f7e569bd54d72daaf683082c96116a4b3fd3597b Mon Sep 17 00:00:00 2001 From: Nicholas Berlette Date: Tue, 23 Dec 2025 15:25:40 -0800 Subject: [PATCH 3/6] chore: switch toolchain back to nightly to use unstable fmt options --- rust-toolchain.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8d3332d..7897570 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] - channel = "stable" + channel = "nightly" components = ["rustfmt", "clippy", "cargo"] - profile = "minimal" \ No newline at end of file + profile = "minimal" From 5ffeb61084888badaa90f6976107dd201f504198 Mon Sep 17 00:00:00 2001 From: Nicholas Berlette Date: Tue, 23 Dec 2025 15:26:21 -0800 Subject: [PATCH 4/6] feat(cow_str): add try_inline, inline, force_inline helpers to CowStr --- src/cow_str.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/cow_str.rs b/src/cow_str.rs index a958354..9dad844 100644 --- a/src/cow_str.rs +++ b/src/cow_str.rs @@ -409,6 +409,77 @@ impl<'i> From> for String { } } +#[cfg(not(feature = "is_variant"))] +impl<'i> CowStr<'i> { + /// Returns `true` if the `CowStr` is the `Owned` variant. + #[inline(always)] + pub const fn is_owned(&self) -> bool { + matches!(self, CowStr::Owned(_)) + } + + /// Returns `true` if the `CowStr` is the `Inlined` variant. + #[inline(always)] + pub const fn is_inlined(&self) -> bool { + matches!(self, CowStr::Inlined(_)) + } + + /// Returns `true` if the `CowStr` is the `Borrowed` variant. + #[inline(always)] + pub const fn is_borrowed(&self) -> bool { + matches!(self, CowStr::Borrowed(_)) + } +} + +impl CowStr<'_> { + /// Attempts to create an inline `CowStr` from a value that can be converted + /// to a string slice via an `AsRef` impl. + /// + /// Returns an error if the string is too long to be inlined. + #[inline(always)] + pub fn try_inline<'i, T: 'i + AsRef>( + s: T, + ) -> Result, StringTooLongError> { + let inline = InlineStr::try_from(s.as_ref())?; + Ok(CowStr::Inlined(inline)) + } + + /// Creates an inline `CowStr` from a value that can be converted to a string + /// slice via an `AsRef` impl, panicking if the string is too long to be + /// inlined. + /// + /// # Panics + /// + /// Panics if the string length exceeds [`MAX_INLINE_STR_LEN`]. + #[inline(always)] + pub fn inline<'i, T: 'i + AsRef>(s: T) -> CowStr<'i> { + let inline = + InlineStr::try_from(s.as_ref()).expect("String too long to inline!"); + CowStr::Inlined(inline) + } + + /// Forcibly creates an inline `CowStr` from a given value that can be + /// converted to a string slice via an `AsRef` impl, truncating it if + /// necessary to fit within the maximum inline length. + #[inline(always)] + pub fn force_inline<'i, T: 'i + AsRef>(s: T) -> CowStr<'i> { + let src = s.as_ref().as_bytes(); + let mut len = src.len(); + if len > MAX_INLINE_STR_LEN { + len = MAX_INLINE_STR_LEN; + } + let mut buf = [0u8; MAX_INLINE_STR_LEN]; + buf[..len].copy_from_slice(&src[..len]); + let len = len as u8; + CowStr::Inlined(InlineStr { buf, len }) + } + + /// Creates an inline `CowStr` from a single character. + #[inline(always)] + pub fn from_char(c: char) -> CowStr<'static> { + CowStr::Inlined(c.into()) + } +} + #[cfg(feature = "serde")] mod serde_impl { use core::fmt; From 517a25a47b8618d35335aa97ed247e3ab4db30ba Mon Sep 17 00:00:00 2001 From: Nicholas Berlette Date: Tue, 23 Dec 2025 15:58:20 -0800 Subject: [PATCH 5/6] fix(cow_str): fix lifetime errors in CowStr's FromStr impl --- src/cow_str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cow_str.rs b/src/cow_str.rs index 9dad844..7d7db99 100644 --- a/src/cow_str.rs +++ b/src/cow_str.rs @@ -173,7 +173,7 @@ impl<'i> FromStr for CowStr<'i> { fn from_str(s: &str) -> Result { match InlineStr::try_from(s) { Ok(inline) => Ok(CowStr::Inlined(inline)), - Err(_) => Ok(CowStr::Borrowed(s)), + Err(_) => Ok(CowStr::Owned(s.to_string().into_boxed_str())), } } } From 7b48c1b202e2075df371f836d4bf9382e99d299b Mon Sep 17 00:00:00 2001 From: Nicholas Berlette Date: Tue, 23 Dec 2025 16:24:26 -0800 Subject: [PATCH 6/6] chore: fmt --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0618acf..15a29c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,18 +15,18 @@ on: inputs: release: type: boolean - description: 'Create a GitHub Release?' + description: "Create a GitHub Release?" default: false required: false publish: type: boolean - description: 'Publish to crates.io?' + description: "Publish to crates.io?" default: false required: false version: type: string - description: 'Version (if $GITHUB_REF is not a tag)' - default: '' + description: "Version (if $GITHUB_REF is not a tag)" + default: "" required: false env: CARGO_TERM_COLOR: always