use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
string::String,
vec::Vec,
};
use smol_str::SmolStr;
use crate::CowStr;
mod internal {
pub trait Ref<T: ?Sized> {
fn cast<'a>(self) -> &'a T
where
Self: 'a;
}
impl<T: ?Sized> Ref<T> for &T {
#[inline]
fn cast<'a>(self) -> &'a T
where
Self: 'a,
{
self
}
}
}
use internal::Ref;
pub trait Bos<T: ?Sized> {
type Ref<'this>: Ref<T>
where
Self: 'this;
fn borrow_or_share(this: &Self) -> Self::Ref<'_>;
}
pub trait BorrowOrShare<'i, 'o, T: ?Sized>: Bos<T> {
fn borrow_or_share(&'i self) -> &'o T;
}
impl<'i, 'o, T: ?Sized, B> BorrowOrShare<'i, 'o, T> for B
where
B: Bos<T> + ?Sized + 'i,
B::Ref<'i>: 'o,
{
#[inline]
fn borrow_or_share(&'i self) -> &'o T {
(B::borrow_or_share(self) as B::Ref<'i>).cast()
}
}
impl<'a, T: ?Sized> Bos<T> for &'a T {
type Ref<'this>
= &'a T
where
Self: 'this;
#[inline]
fn borrow_or_share(this: &Self) -> Self::Ref<'_> {
this
}
}
#[macro_export]
macro_rules! impl_bos {
($($(#[$attr:meta])? $({$($params:tt)*})? $ty:ty => $target:ty)*) => {
$(
$(#[$attr])?
impl $(<$($params)*>)? $crate::bos::Bos<$target> for $ty {
type Ref<'this> = &'this $target where Self: 'this;
#[inline]
fn borrow_or_share(this: &Self) -> Self::Ref<'_> {
this
}
}
)*
};
}
impl_bos! {
{T: ?Sized} &mut T => T
{T, const N: usize} [T; N] => [T]
{T} Vec<T> => [T]
String => str
{T: ?Sized} Box<T> => T
{B: ?Sized + ToOwned} Cow<'_, B> => B
{T: ?Sized} alloc::sync::Arc<T> => T
{T: ?Sized} alloc::rc::Rc<T> => T
}
#[cfg(feature = "std")]
impl_bos! {
std::ffi::OsString => std::ffi::OsStr
std::path::PathBuf => std::path::Path
alloc::ffi::CString => core::ffi::CStr
}
impl_bos! {
SmolStr => str
}
impl<'a> Bos<str> for CowStr<'a> {
type Ref<'this>
= &'this str
where
Self: 'this;
#[inline]
fn borrow_or_share(this: &Self) -> Self::Ref<'_> {
this.as_str()
}
}
pub type DefaultStr = SmolStr;
pub trait FromStaticStr {
fn from_static(s: &'static str) -> Self;
}
impl FromStaticStr for SmolStr {
#[inline]
fn from_static(s: &'static str) -> Self {
SmolStr::new_static(s)
}
}
impl FromStaticStr for String {
#[inline]
fn from_static(s: &'static str) -> Self {
String::from(s)
}
}
impl<'a> FromStaticStr for CowStr<'a> {
#[inline]
fn from_static(s: &'static str) -> Self {
CowStr::new_static(s)
}
}
impl<'a> FromStaticStr for Cow<'a, str> {
#[inline]
fn from_static(s: &'static str) -> Self {
Cow::Borrowed(s)
}
}
impl<'a> FromStaticStr for &'a str {
#[inline]
fn from_static(s: &'a str) -> Self {
s
}
}
pub trait BosStr: Bos<str> + AsRef<str> + FromStaticStr {}
impl<T> BosStr for T where T: Bos<str> + AsRef<str> + FromStaticStr {}
#[cfg(test)]
mod tests {
use super::*;
fn as_str_via_bos<'i, 'o, S: BorrowOrShare<'i, 'o, str>>(s: &'i S) -> &'o str {
s.borrow_or_share()
}
#[test]
fn bos_smolstr() {
let s = SmolStr::new("hello");
assert_eq!(as_str_via_bos(&s), "hello");
}
#[test]
fn bos_string() {
let s = String::from("hello");
assert_eq!(as_str_via_bos(&s), "hello");
}
#[test]
fn bos_ref_str() {
let s: &str = "hello";
assert_eq!(as_str_via_bos(&s), "hello");
}
#[test]
fn bos_cowstr_borrowed() {
let s = CowStr::Borrowed("hello");
assert_eq!(as_str_via_bos(&s), "hello");
}
#[test]
fn bos_cowstr_owned() {
let s = CowStr::Owned(SmolStr::new("hello"));
assert_eq!(as_str_via_bos(&s), "hello");
}
fn as_ref_via_bos<S: Bos<str>>(s: &S) -> &str {
let r = S::borrow_or_share(s);
r.cast()
}
#[test]
fn bos_as_ref_smolstr() {
let s = SmolStr::new("world");
assert_eq!(as_ref_via_bos(&s), "world");
}
#[test]
fn bos_as_ref_ref_str() {
let s: &str = "world";
assert_eq!(as_ref_via_bos(&s), "world");
}
#[test]
fn ref_str_sharing_outlives_wrapper() {
let original: &str = "shared";
let result: &str;
{
let wrapper: &&str = &original;
result = as_str_via_bos(wrapper);
}
assert_eq!(result, "shared");
}
}