use alloc::{
borrow::Cow,
ffi::{CString, IntoStringError},
rc::Rc,
string::{FromUtf8Error, String},
sync::Arc,
vec::Vec,
};
use core::{
convert::Infallible,
str::{FromStr, Utf8Error},
};
#[cfg(feature = "std")]
use std::{ffi::OsStr, path::Path};
use crate::flex::{FlexStr, RefCounted, RefCountedMut, partial_eq_impl, ref_counted_mut_impl};
use flexstr_support::StringToFromBytes;
pub type LocalStr = FlexStr<'static, str, Rc<str>>;
pub type SharedStr = FlexStr<'static, str, Arc<str>>;
pub type LocalStrRef<'s> = FlexStr<'s, str, Rc<str>>;
pub type SharedStrRef<'s> = FlexStr<'s, str, Arc<str>>;
const _: () = assert!(
size_of::<Option<LocalStr>>() <= size_of::<String>(),
"Option<LocalStr> must be less than or equal to the size of String"
);
const _: () = assert!(
size_of::<Option<SharedStr>>() <= size_of::<String>(),
"Option<SharedStr> must be less than or equal to the size of String"
);
ref_counted_mut_impl!(str);
impl<'s, R: RefCounted<str>> From<String> for FlexStr<'s, str, R> {
fn from(s: String) -> Self {
FlexStr::from_owned(s)
}
}
impl<'s, R: RefCounted<str>> TryFrom<&'s [u8]> for FlexStr<'s, str, R> {
type Error = Utf8Error;
#[inline]
fn try_from(s: &'s [u8]) -> Result<Self, Self::Error> {
Ok(FlexStr::from_borrowed(str::from_utf8(s)?))
}
}
#[cfg(feature = "std")]
impl<'s, R: RefCounted<str>> TryFrom<&'s OsStr> for FlexStr<'s, str, R> {
type Error = Utf8Error;
#[inline]
fn try_from(s: &'s OsStr) -> Result<Self, Self::Error> {
Ok(FlexStr::from_borrowed(s.try_into()?))
}
}
#[cfg(feature = "std")]
impl<'s, R: RefCounted<str>> TryFrom<&'s Path> for FlexStr<'s, str, R> {
type Error = Utf8Error;
#[inline]
fn try_from(s: &'s Path) -> Result<Self, Self::Error> {
Ok(FlexStr::from_borrowed(s.as_os_str().try_into()?))
}
}
impl<R: RefCounted<str>> TryFrom<Vec<u8>> for FlexStr<'static, str, R> {
type Error = FromUtf8Error;
#[inline]
fn try_from(s: Vec<u8>) -> Result<Self, Self::Error> {
Ok(FlexStr::from_owned(s.try_into()?))
}
}
impl<R: RefCounted<str>> TryFrom<CString> for FlexStr<'static, str, R> {
type Error = IntoStringError;
#[inline]
fn try_from(s: CString) -> Result<Self, Self::Error> {
Ok(FlexStr::from_owned(s.try_into()?))
}
}
partial_eq_impl!(str, str);
partial_eq_impl!(&str, str);
partial_eq_impl!(String, str);
partial_eq_impl!(Cow<'s, str>, str);
impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> AsRef<str> for FlexStr<'s, S, R>
where
S: AsRef<str>,
{
fn as_ref(&self) -> &str {
self.as_ref_type().as_ref()
}
}
impl<R: RefCounted<str>> FromStr for FlexStr<'static, str, R> {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(FlexStr::from_borrowed(s).into_owned())
}
}
#[cfg(feature = "prost")]
#[allow(deprecated)] impl<R: RefCounted<str>> prost::Message for FlexStr<'static, str, R>
where
Self: Default + Send + Sync,
{
fn encode_raw(&self, buf: &mut impl prost::bytes::BufMut)
where
Self: Sized,
{
buf.put_slice(self.as_ref_type().as_bytes());
}
fn merge_field(
&mut self,
tag: u32,
wire_type: prost::encoding::WireType,
buf: &mut impl prost::bytes::Buf,
ctx: prost::encoding::DecodeContext,
) -> Result<(), prost::DecodeError>
where
Self: Sized,
{
prost::encoding::skip_field(wire_type, tag, buf, ctx)
}
fn encoded_len(&self) -> usize {
self.as_ref_type().len()
}
fn clear(&mut self) {
*self = Default::default();
}
fn merge(&mut self, mut buf: impl prost::bytes::Buf) -> Result<(), prost::DecodeError>
where
Self: Sized,
{
let bytes = buf.copy_to_bytes(buf.remaining());
let s = core::str::from_utf8(&bytes)
.map_err(|_| prost::DecodeError::new("invalid UTF-8 in string field"))?;
*self = FlexStr::from_borrowed(s).into_owned();
Ok(())
}
fn merge_length_delimited(
&mut self,
mut buf: impl prost::bytes::Buf,
) -> Result<(), prost::DecodeError>
where
Self: Sized,
{
let len = prost::encoding::decode_varint(&mut buf)? as usize;
if buf.remaining() < len {
return Err(prost::DecodeError::new("buffer underflow"));
}
self.merge(buf.take(len))
}
}
#[cfg(feature = "sqlx")]
impl<'r, 's, DB: sqlx::Database, R: RefCounted<str>> sqlx::Decode<'r, DB> for FlexStr<'s, str, R>
where
&'r str: sqlx::Decode<'r, DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
let value = <&str as sqlx::Decode<DB>>::decode(value)?;
let s: FlexStr<'_, str, R> = value.into();
Ok(s.into_owned())
}
}
#[cfg(feature = "sqlx")]
impl<'r, 's, DB: sqlx::Database, R: RefCounted<str>> sqlx::Encode<'r, DB> for FlexStr<'s, str, R>
where
String: sqlx::Encode<'r, DB>,
{
fn encode_by_ref(
&self,
buf: &mut <DB as sqlx::Database>::ArgumentBuffer<'r>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
<String as sqlx::Encode<'r, DB>>::encode(self.to_string(), buf)
}
fn encode(
self,
buf: &mut <DB as sqlx::Database>::ArgumentBuffer<'r>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError>
where
Self: Sized,
{
use flexstr_support::StringLike as _;
<String as sqlx::Encode<'r, DB>>::encode(self.into_string(), buf)
}
fn size_hint(&self) -> usize {
self.len()
}
}
#[cfg(feature = "sqlx")]
impl<'s, DB: sqlx::Database, R: RefCounted<str>> sqlx::Type<DB> for FlexStr<'s, str, R>
where
str: sqlx::Type<DB>,
{
fn type_info() -> <DB as sqlx::Database>::TypeInfo {
<str as sqlx::Type<DB>>::type_info()
}
fn compatible(ty: &<DB as sqlx::Database>::TypeInfo) -> bool {
<str as sqlx::Type<DB>>::compatible(ty)
}
}
#[cfg(all(feature = "sqlx", feature = "sqlx_pg_arrays"))]
impl<'s, R: RefCounted<str>> sqlx::postgres::PgHasArrayType for FlexStr<'s, str, R>
where
for<'a> &'a str: sqlx::postgres::PgHasArrayType,
{
fn array_type_info() -> sqlx::postgres::PgTypeInfo {
<&str as sqlx::postgres::PgHasArrayType>::array_type_info()
}
fn array_compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
<&str as sqlx::postgres::PgHasArrayType>::array_compatible(ty)
}
}
#[cfg(feature = "utoipa")]
impl<'s, R: RefCounted<str>> utoipa::PartialSchema for FlexStr<'s, str, R> {
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ObjectBuilder::new()
.schema_type(utoipa::openapi::schema::SchemaType::new(
utoipa::openapi::schema::Type::String,
))
.into()
}
}
#[cfg(feature = "utoipa")]
impl<'s, R: RefCounted<str>> utoipa::ToSchema for FlexStr<'s, str, R> {
fn name() -> Cow<'static, str> {
Cow::Borrowed("String")
}
}