diff --git a/src/lib.rs b/src/lib.rs index 89b35da..72b2037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(unknown_lints)] // `warn(rustdoc::all)` triggers this for weird nightly reasons +#![allow(unknown_lints)] // `warn(rustdoc::all)` triggers this for some reason #![warn(rustdoc::all)] #![warn(clippy::all)] #![warn(clippy::pedantic)] @@ -21,14 +21,17 @@ use std::{ /// A smaller [`Cow<'static, str>`](Cow). /// -/// This type can represent either an [`&'static str`](prim@str), or a [`String`], and fits within 3 words -/// (the same size as `String`). It does this by storing a capacity of `0` when it is borrowed. -/// However, this does mean that it is impossible to distinguish between borrowed and owned when -/// `capacity == len == 0`, but in that case it really doesn't matter much anyway since an owned -/// `String` with capacity equal to zero has not yet allocated. +/// This type can represent either an [`&'static str`](prim@str), or a [`String`], and fits within +/// 3 words (the same size as `String`). It does this by storing a capacity of `0` when it is +/// borrowed. However, this does mean that it is impossible to distinguish between borrowed and +/// owned when `capacity == len == 0`, but in that case it really doesn't matter much anyway since +/// an owned `String` with capacity equal to zero has not yet allocated. /// -/// When doing non-mutating actions such as [`Deref::deref`], the string remains in whatever state it -/// was in. When doing mutating actions such as [`DerefMut::deref_mut`], the string becomes owned. +/// Note that this method of distinguishing a borrowed string from an owned string still allows +/// storing a [`NonNull`] pointer, So this type is *still* 3 words when in an [`Option`]. +/// +/// When doing non-mutating actions such as [`Deref::deref`], the string remains in whatever state +/// it was in. When doing mutating actions such as [`DerefMut::deref_mut`], the string is made owned. /// /// ## Mutation as a `String` /// @@ -149,13 +152,9 @@ impl Stringish { /// If the `Stringish` is borrowed, the data is copied into a new allocation and the /// `Stringish` becomes owned. Otherwise, nothing happens. pub fn make_owned(&mut self) { - if let None | Some(true) = self.is_owned() { - return; + if let Some(s) = self.as_static_str() { + *self = Stringish::new_owned(String::from(s)); } - - let mut s = String::with_capacity(self.len); - s.push_str(self); - *self = Stringish::new_owned(s); } /// Converts a `Stringish` into an owned [`String`]. @@ -171,7 +170,48 @@ impl Stringish { unsafe { String::from_raw_parts(this.ptr.as_ptr(), this.len, this.cap) } } - // Reborrowing + /// Returns the inner [`&'static str`](prim@str) if the `Stringish` is borrowed. + #[must_use] + pub fn as_static_str(&self) -> Option<&'static str> { + if let None | Some(true) = self.is_borrowed() { + // SAFETY: if we are borrowed then we borrow a `&'static str`, so lifetime extension is safe + Some(unsafe { &*(self.as_str() as *const str) }) + } else { + None + } + } + + /// Converts the `Stringish` into a [`Cow<'static, str>`](Cow). + /// + /// This is the inverse of [`Stringish::from_cow`] + #[must_use] + pub fn into_cow(self) -> Cow<'static, str> { + if let Some(s) = self.as_static_str() { + Cow::Borrowed(s) + } else { + Cow::Owned(self.into_owned()) + } + } + + /// Consumes and leaks the `Stringish`, returning an immutable reference to the contents. + /// + /// If the `Stringish` is borrowed, no allocation needs to be created since it already contains + /// a [`&'static str`](prim@str). + /// + /// If you wish to get a mutable reference, see [`String::leak`]. + #[must_use] + pub fn leak(self) -> &'static str { + if let Some(s) = self.as_static_str() { + s + } else { + // SAFETY: workaround for `String::leak` being unstable + unsafe { str::from_utf8_unchecked(self.into_owned().into_bytes().leak()) } + } + } + + ///////////////// + // Reborrowing // + ///////////////// /// Returns a byte slice of this `Stringish`'s contents. #[must_use] @@ -239,6 +279,15 @@ impl From> for Stringish { } } +/// Converts a `Stringish` into a [`Cow<'static, str>`](Cow). +/// +/// See also: [`Stringish::into_cow`] +impl From for Cow<'static, str> { + fn from(stringish: Stringish) -> Self { + stringish.into_cow() + } +} + impl Deref for Stringish { type Target = str; @@ -254,7 +303,9 @@ impl DerefMut for Stringish { } impl Stringish { - // Mutation as a `String` + //////////////////////////// + // Mutation as a `String` // + //////////////////////////// /// Allows mutation of this `Stringish` as a `String` using a guard. ///