use alloc::borrow::{Borrow, Cow};
#[cfg(not(feature = "std"))]
use alloc::{borrow::ToOwned, boxed::Box};
use alloc::{rc::Rc, sync::Arc};
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::ops::{Deref, Index};
use core::slice::SliceIndex;
#[cfg(feature = "std")]
use std::{io, net::ToSocketAddrs};
use flexstr_support::{StringFromBytesMut, StringLike, StringToFromBytes};
use inline_flexstr::InlineFlexStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
macro_rules! partial_eq_impl {
($type:ty, $str_type:ty) => {
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> PartialEq<$type>
for FlexStr<'s, S, R>
where
S: PartialEq<$str_type>,
{
fn eq(&self, other: &$type) -> bool {
S::eq(self, other)
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> PartialEq<FlexStr<'s, S, R>>
for $type
where
S: PartialEq<$str_type>,
{
fn eq(&self, other: &FlexStr<'s, S, R>) -> bool {
S::eq(other, self)
}
}
};
}
pub(crate) use partial_eq_impl;
macro_rules! ref_counted_mut_impl {
($str_type:ty) => {
impl RefCountedMut<$str_type> for Arc<$str_type> {
#[inline]
fn to_mut(&mut self) -> &mut $str_type {
Arc::make_mut(self)
}
#[inline]
fn as_mut(&mut self) -> &mut $str_type {
Arc::get_mut(self).expect("Arc is shared")
}
#[inline]
fn try_as_mut(&mut self) -> Option<&mut $str_type> {
Arc::get_mut(self)
}
}
impl RefCountedMut<$str_type> for Rc<$str_type> {
#[inline]
fn to_mut(&mut self) -> &mut $str_type {
Rc::make_mut(self)
}
#[inline]
fn as_mut(&mut self) -> &mut $str_type {
Rc::get_mut(self).expect("Rc is shared")
}
#[inline]
fn try_as_mut(&mut self) -> Option<&mut $str_type> {
Rc::get_mut(self)
}
}
};
}
pub(crate) use ref_counted_mut_impl;
pub trait ImmutableBytes: StringToFromBytes {}
pub trait RefCounted<S: ?Sized + StringToFromBytes>:
Deref<Target = S> + for<'a> From<&'a S> + Clone
{
}
impl<S, R> RefCounted<S> for R
where
R: Deref<Target = S> + for<'a> From<&'a S> + Clone,
S: ?Sized + StringToFromBytes,
{
}
pub trait RefCountedMut<S: ?Sized + StringToFromBytes>: RefCounted<S> {
fn to_mut(&mut self) -> &mut S;
fn as_mut(&mut self) -> &mut S;
fn try_as_mut(&mut self) -> Option<&mut S> {
None
}
}
pub trait ToOwnedFlexStr<R, S>
where
R: RefCounted<S>,
S: ?Sized + StringToFromBytes,
{
fn to_owned_opt(&self) -> FlexStr<'static, S, R>;
}
impl<R, S> ToOwnedFlexStr<R, S> for S
where
R: RefCounted<S>,
S: ?Sized + StringToFromBytes,
{
fn to_owned_opt(&self) -> FlexStr<'static, S, R> {
FlexStr::from_borrowed(self).into_owned()
}
}
pub trait IntoOptimizedFlexStr<R, S>
where
R: RefCounted<S>,
S: ?Sized + StringToFromBytes,
Box<S>: From<S::Owned>,
{
fn into_opt(self) -> FlexStr<'static, S, R>;
}
impl<R, S> IntoOptimizedFlexStr<R, S> for S::Owned
where
R: RefCounted<S>,
S: ?Sized + StringToFromBytes,
Box<S>: From<S::Owned>,
{
fn into_opt(self) -> FlexStr<'static, S, R> {
FlexStr::from_owned(self).optimize()
}
}
#[doc(alias = "SharedStr")]
#[doc(alias = "LocalStr")]
#[doc(alias = "SharedOsStr")]
#[doc(alias = "LocalOsStr")]
#[doc(alias = "SharedPath")]
#[doc(alias = "LocalPath")]
#[doc(alias = "SharedCStr")]
#[doc(alias = "LocalCStr")]
#[doc(alias = "SharedBytes")]
#[doc(alias = "LocalBytes")]
#[derive(Debug)]
pub enum FlexStr<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> {
Borrowed(&'s S),
Inlined(InlineFlexStr<S>),
RefCounted(R),
Boxed(Box<S>),
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> FlexStr<'s, S, R>
where
for<'a> &'a S: Default,
{
pub fn empty() -> FlexStr<'s, S, R> {
FlexStr::Borrowed(Default::default())
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> FlexStr<'s, S, R> {
fn copy(&self) -> FlexStr<'s, S, R> {
match self {
FlexStr::Borrowed(s) => FlexStr::Borrowed(s),
FlexStr::Inlined(s) => FlexStr::Inlined(*s),
FlexStr::RefCounted(s) => FlexStr::RefCounted(s.clone()),
FlexStr::Boxed(s) => FlexStr::copy_into_owned(s),
}
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> FlexStr<'s, S, R>
where
Box<S>: From<S::Owned>,
{
pub fn from_owned(s: S::Owned) -> FlexStr<'static, S, R> {
FlexStr::Boxed(s.into())
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> FlexStr<'s, S, R> {
pub const fn from_borrowed(s: &'s S) -> FlexStr<'s, S, R> {
FlexStr::Borrowed(s)
}
pub fn from_inline(s: InlineFlexStr<S>) -> FlexStr<'s, S, R> {
FlexStr::Inlined(s)
}
pub fn from_ref_counted(s: R) -> FlexStr<'s, S, R> {
FlexStr::RefCounted(s)
}
pub fn from_boxed(s: Box<S>) -> FlexStr<'s, S, R> {
FlexStr::Boxed(s)
}
pub fn is_borrowed(&self) -> bool {
matches!(self, FlexStr::Borrowed(_))
}
pub fn is_inlined(&self) -> bool {
matches!(self, FlexStr::Inlined(_))
}
pub fn is_ref_counted(&self) -> bool {
matches!(self, FlexStr::RefCounted(_))
}
pub fn is_boxed(&self) -> bool {
matches!(self, FlexStr::Boxed(_))
}
pub fn is_on_heap(&self) -> bool {
matches!(self, FlexStr::RefCounted(_) | FlexStr::Boxed(_))
}
pub fn is_off_heap(&self) -> bool {
matches!(self, FlexStr::Borrowed(_) | FlexStr::Inlined(_))
}
fn copy_into_owned(s: &S) -> FlexStr<'static, S, R> {
match InlineFlexStr::try_from_type(s) {
Ok(inline) => FlexStr::Inlined(inline),
Err(_) => FlexStr::RefCounted(s.into()),
}
}
pub fn optimize(self) -> FlexStr<'s, S, R> {
match self {
FlexStr::Boxed(s) => Self::copy_into_owned(&s),
FlexStr::RefCounted(s) => match InlineFlexStr::try_from_type(&*s) {
Ok(inline) => FlexStr::Inlined(inline),
Err(_) => FlexStr::RefCounted(s),
},
_ => self,
}
}
pub fn to_owned(&self) -> FlexStr<'static, S, R> {
match self {
FlexStr::Borrowed(s) => FlexStr::copy_into_owned(s),
FlexStr::Inlined(s) => FlexStr::Inlined(*s),
FlexStr::RefCounted(s) => FlexStr::RefCounted(s.clone()),
FlexStr::Boxed(s) => FlexStr::copy_into_owned(s),
}
}
pub fn into_owned(self) -> FlexStr<'static, S, R> {
match self {
FlexStr::Borrowed(s) => FlexStr::copy_into_owned(s),
FlexStr::Inlined(s) => FlexStr::Inlined(s),
FlexStr::RefCounted(s) => FlexStr::RefCounted(s),
FlexStr::Boxed(s) => FlexStr::Boxed(s),
}
}
pub fn as_ref_type(&self) -> &S {
match self {
FlexStr::Borrowed(s) => s,
FlexStr::Inlined(s) => s,
FlexStr::RefCounted(s) => s,
FlexStr::Boxed(s) => s,
}
}
pub fn to_owned_type(&self) -> S::Owned {
match self {
FlexStr::Borrowed(s) => <S as ToOwned>::to_owned(s),
FlexStr::Inlined(s) => <S as ToOwned>::to_owned(s),
FlexStr::RefCounted(s) => <S as ToOwned>::to_owned(s),
FlexStr::Boxed(s) => <S as ToOwned>::to_owned(s),
}
}
pub fn as_raw_bytes(&self) -> &[u8] {
match self {
FlexStr::Borrowed(s) => S::self_as_raw_bytes(s),
FlexStr::Inlined(s) => s.as_raw_bytes(),
FlexStr::RefCounted(s) => S::self_as_raw_bytes(s),
FlexStr::Boxed(s) => S::self_as_raw_bytes(s),
}
}
pub fn as_bytes(&self) -> &[u8] {
match self {
FlexStr::Borrowed(s) => S::self_as_bytes(s),
FlexStr::Inlined(s) => s.as_bytes(),
FlexStr::RefCounted(s) => S::self_as_bytes(s),
FlexStr::Boxed(s) => S::self_as_bytes(s),
}
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> FlexStr<'s, S, R>
where
S::Owned: From<Box<S>>,
{
pub fn into_owned_type(self) -> S::Owned {
match self {
FlexStr::Borrowed(s) => <S as ToOwned>::to_owned(s),
FlexStr::Inlined(s) => <S as ToOwned>::to_owned(&s),
FlexStr::RefCounted(s) => <S as ToOwned>::to_owned(&s),
FlexStr::Boxed(s) => s.into(),
}
}
}
impl<'s, S: ?Sized + ImmutableBytes, R: RefCountedMut<S>> FlexStr<'s, S, R> {
pub fn to_mut_type_fallback(&mut self) -> &mut S {
match self {
FlexStr::Borrowed(s) => {
*self = FlexStr::RefCounted((&**s).into());
match self {
FlexStr::RefCounted(s) => s.as_mut(),
_ => unreachable!("Unexpected variant"),
}
}
FlexStr::Inlined(s) => {
*self = FlexStr::RefCounted((&**s).into());
match self {
FlexStr::RefCounted(s) => s.as_mut(),
_ => unreachable!("Unexpected variant"),
}
}
FlexStr::RefCounted(s) => s.to_mut(),
FlexStr::Boxed(s) => s.as_mut(),
}
}
}
impl<'s, S: ?Sized + StringFromBytesMut, R: RefCountedMut<S>> FlexStr<'s, S, R> {
pub fn to_mut_type(&mut self) -> &mut S {
match self {
FlexStr::Borrowed(s) => {
*self = FlexStr::copy_into_owned(s);
match self {
FlexStr::Inlined(s) => s.as_mut_type(),
FlexStr::RefCounted(s) => s.as_mut(),
_ => {
unreachable!("Unexpected borrowed/boxed variant");
}
}
}
FlexStr::Inlined(s) => s.as_mut_type(),
FlexStr::RefCounted(s) => s.to_mut(),
FlexStr::Boxed(s) => s.as_mut(),
}
}
}
impl<'s, S: ?Sized + StringToFromBytes> FlexStr<'s, S, Arc<S>>
where
Arc<S>: for<'a> From<&'a S>,
Rc<S>: for<'a> From<&'a S>,
{
pub fn to_local(&self) -> FlexStr<'s, S, Rc<S>> {
match self {
FlexStr::Borrowed(s) => FlexStr::Borrowed(s),
FlexStr::Inlined(s) => FlexStr::Inlined(*s),
FlexStr::RefCounted(s) => FlexStr::RefCounted(Rc::from(s)),
FlexStr::Boxed(s) => FlexStr::copy_into_owned(s),
}
}
pub fn into_local(self) -> FlexStr<'s, S, Rc<S>> {
match self {
FlexStr::Borrowed(s) => FlexStr::Borrowed(s),
FlexStr::Inlined(s) => FlexStr::Inlined(s),
FlexStr::RefCounted(s) => FlexStr::RefCounted(Rc::from(&s)),
FlexStr::Boxed(s) => FlexStr::Boxed(s),
}
}
}
impl<'s, S: ?Sized + StringToFromBytes> FlexStr<'s, S, Rc<S>>
where
Rc<S>: for<'a> From<&'a S>,
Arc<S>: for<'a> From<&'a S>,
{
pub fn to_shared(&self) -> FlexStr<'s, S, Arc<S>> {
match self {
FlexStr::Borrowed(s) => FlexStr::Borrowed(s),
FlexStr::Inlined(s) => FlexStr::Inlined(*s),
FlexStr::RefCounted(s) => FlexStr::RefCounted(Arc::from(s)),
FlexStr::Boxed(s) => FlexStr::copy_into_owned(s),
}
}
pub fn into_shared(self) -> FlexStr<'s, S, Arc<S>> {
match self {
FlexStr::Borrowed(s) => FlexStr::Borrowed(s),
FlexStr::Inlined(s) => FlexStr::Inlined(s),
FlexStr::RefCounted(s) => FlexStr::RefCounted(Arc::from(&s)),
FlexStr::Boxed(s) => FlexStr::Boxed(s),
}
}
}
impl<S: ?Sized + StringToFromBytes, R: RefCounted<S>> StringLike<S> for FlexStr<'_, S, R> {
fn as_ref_type(&self) -> &S {
<Self>::as_ref_type(self)
}
fn as_bytes(&self) -> &[u8] {
<Self>::as_bytes(self)
}
fn into_owned_type(self) -> S::Owned
where
S::Owned: From<Box<S>>,
{
<Self>::into_owned_type(self)
}
fn to_owned_type(&self) -> S::Owned {
<Self>::to_owned_type(self)
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Default for FlexStr<'s, S, R>
where
for<'a> &'a S: Default,
{
fn default() -> FlexStr<'s, S, R> {
FlexStr::empty()
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> From<&'s S> for FlexStr<'s, S, R> {
fn from(s: &'s S) -> Self {
FlexStr::from_borrowed(s)
}
}
impl<'s, S: ?Sized + StringToFromBytes> From<Rc<S>> for FlexStr<'s, S, Rc<S>>
where
Rc<S>: for<'a> From<&'a S>,
{
fn from(s: Rc<S>) -> Self {
FlexStr::from_ref_counted(s)
}
}
impl<'s, S: ?Sized + StringToFromBytes> From<Arc<S>> for FlexStr<'s, S, Arc<S>>
where
Arc<S>: for<'a> From<&'a S>,
{
fn from(s: Arc<S>) -> Self {
FlexStr::from_ref_counted(s)
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> From<Box<S>> for FlexStr<'s, S, R> {
fn from(s: Box<S>) -> Self {
FlexStr::from_boxed(s)
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> From<InlineFlexStr<S>>
for FlexStr<'s, S, R>
{
fn from(s: InlineFlexStr<S>) -> Self {
FlexStr::from_inline(s)
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> From<Cow<'s, S>> for FlexStr<'s, S, R>
where
Box<S>: From<S::Owned>,
{
fn from(s: Cow<'s, S>) -> Self {
match s {
Cow::Borrowed(s) => FlexStr::from_borrowed(s),
Cow::Owned(s) => FlexStr::from_owned(s),
}
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Clone for FlexStr<'s, S, R> {
fn clone(&self) -> Self {
self.copy()
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Hash for FlexStr<'s, S, R>
where
S: Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref_type().hash(state);
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Deref for FlexStr<'s, S, R> {
type Target = S;
fn deref(&self) -> &Self::Target {
self.as_ref_type()
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> fmt::Display for FlexStr<'s, S, R>
where
S: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
S::fmt(self.as_ref_type(), f)
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Borrow<S> for FlexStr<'s, S, R> {
fn borrow(&self) -> &S {
self.as_ref_type()
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> PartialEq for FlexStr<'s, S, R>
where
S: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
S::eq(self.as_ref_type(), other.as_ref_type())
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Eq for FlexStr<'s, S, R> where S: Eq {}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> PartialOrd for FlexStr<'s, S, R>
where
S: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
S::partial_cmp(self.as_ref_type(), other.as_ref_type())
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Ord for FlexStr<'s, S, R>
where
S: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
S::cmp(self.as_ref_type(), other.as_ref_type())
}
}
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>, I: SliceIndex<S>> Index<I>
for FlexStr<'s, S, R>
where
S: Index<I>,
{
type Output = S::Output;
fn index(&self, index: I) -> &Self::Output {
S::index(self.as_ref_type(), index)
}
}
#[cfg(feature = "std")]
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> ToSocketAddrs for FlexStr<'s, S, R>
where
S: ToSocketAddrs,
{
type Iter = <S as ToSocketAddrs>::Iter;
fn to_socket_addrs(&self) -> io::Result<<S as ToSocketAddrs>::Iter> {
self.as_ref_type().to_socket_addrs()
}
}
#[cfg(feature = "serde")]
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Serialize for FlexStr<'s, S, R>
where
S: Serialize,
{
fn serialize<SER: Serializer>(&self, serializer: SER) -> Result<SER::Ok, SER::Error> {
S::serialize(self.as_ref_type(), serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, S: ?Sized + StringToFromBytes, R: RefCounted<S>> Deserialize<'de>
for FlexStr<'static, S, R>
where
Box<S>: Deserialize<'de>,
{
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Box::deserialize(deserializer)
.map(FlexStr::Boxed)
.map(FlexStr::optimize)
}
}
#[cfg(feature = "zeroize")]
fn zeroize_raw_bytes<S: ?Sized + StringToFromBytes>(s: &mut S) {
let len = S::self_as_raw_bytes(s).len();
let ptr = s as *mut S as *mut u8;
unsafe {
zeroize::Zeroize::zeroize(core::slice::from_raw_parts_mut(ptr, len));
}
}
#[cfg(feature = "zeroize")]
impl<'s, S: ?Sized + StringToFromBytes, R: RefCountedMut<S>> zeroize::TryZeroize
for FlexStr<'s, S, R>
{
fn try_zeroize(&mut self) -> bool {
match self {
FlexStr::Inlined(s) => {
zeroize::Zeroize::zeroize(s);
}
FlexStr::Boxed(s) => {
zeroize_raw_bytes(&mut **s);
}
FlexStr::RefCounted(rc) => {
if let Some(s) = rc.try_as_mut() {
zeroize_raw_bytes(s);
} else {
return false;
}
}
FlexStr::Borrowed(_) => return false,
}
*self = FlexStr::Inlined(InlineFlexStr::zeroed());
true
}
}