use std::borrow::Cow;
use std::fmt::Display;
use std::hash::Hash;
use bytestring::ByteString;
#[cfg(feature = "serde")]
pub(crate) mod serde;
#[derive(Debug, Clone, Eq)]
pub enum StringCow<'a> {
Ref(&'a str),
StaticRef(&'static str),
String(String),
Bytes(ByteString),
}
impl Default for StringCow<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> StringCow<'a> {
pub fn new() -> Self {
Self::from_static("")
}
pub fn from_static(slice: &'static str) -> Self {
StringCow::StaticRef(slice)
}
pub fn from_bytes(bytes: ByteString) -> Self {
StringCow::Bytes(bytes)
}
pub fn from_cow(cow: Cow<'a, str>) -> Self {
match cow {
Cow::Borrowed(slice) => StringCow::Ref(slice),
Cow::Owned(string) => StringCow::String(string),
}
}
pub fn from_ref(slice: &'a str) -> Self {
StringCow::Ref(slice)
}
pub fn from_string(string: String) -> Self {
StringCow::String(string)
}
pub fn into_bytes(self) -> ByteString {
match self {
StringCow::Ref(slice) => ByteString::from(slice),
StringCow::StaticRef(slice) => ByteString::from_static(slice),
StringCow::String(string) => ByteString::from(string),
StringCow::Bytes(bytes) => bytes,
}
}
pub fn into_owned(self) -> StringCow<'static> {
match self {
StringCow::Ref(slice) => StringCow::from(slice.to_owned()),
StringCow::StaticRef(slice) => StringCow::StaticRef(slice),
StringCow::String(string) => StringCow::String(string),
StringCow::Bytes(bytes) => StringCow::Bytes(bytes),
}
}
pub fn as_str(&self) -> &str {
match self {
StringCow::Ref(slice) => slice,
StringCow::StaticRef(slice) => slice,
StringCow::String(string) => string.as_str(),
StringCow::Bytes(bytes) => bytes.as_ref(),
}
}
}
impl PartialEq<str> for StringCow<'_> {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl Hash for StringCow<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl PartialOrd for StringCow<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for StringCow<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl<T> PartialEq<T> for StringCow<'_>
where
T: AsRef<str>,
{
fn eq(&self, other: &T) -> bool {
self.as_str() == other.as_ref()
}
}
impl Display for StringCow<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StringCow::Ref(slice) => slice.fmt(f),
StringCow::StaticRef(slice) => slice.fmt(f),
StringCow::String(string) => string.fmt(f),
StringCow::Bytes(bytes) => bytes.fmt(f),
}
}
}
impl AsRef<str> for StringCow<'_> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<'a> From<Cow<'a, str>> for StringCow<'a> {
fn from(cow: Cow<'a, str>) -> Self {
StringCow::from_cow(cow)
}
}
impl<'a> From<&'a str> for StringCow<'a> {
fn from(slice: &'a str) -> Self {
StringCow::from_ref(slice)
}
}
impl From<String> for StringCow<'_> {
fn from(string: String) -> Self {
StringCow::from_string(string)
}
}
impl From<ByteString> for StringCow<'_> {
fn from(bytes: ByteString) -> Self {
StringCow::from_bytes(bytes)
}
}
#[cfg(test)]
#[cfg_attr(all(test, coverage_nightly), coverage(off))]
mod tests {
use bytestring::ByteString;
use super::StringCow;
#[test]
fn constructors() {
let cow = StringCow::default();
assert_eq!(cow.as_str(), "");
let cow = StringCow::from_static("hello");
assert_eq!(cow.as_str(), "hello");
let cow = StringCow::from_ref("world");
assert_eq!(cow.as_str(), "world");
let cow = StringCow::from_string(String::from("foo"));
assert_eq!(cow.as_str(), "foo");
let cow = StringCow::from(String::from("bar"));
assert_eq!(cow.as_str(), "bar");
let cow = StringCow::from_bytes(ByteString::from_static("foo"));
assert_eq!(cow.as_str(), "foo");
let cow = StringCow::from(ByteString::from_static("foo"));
assert_eq!(cow.as_str(), "foo");
let cow = StringCow::from_cow(std::borrow::Cow::Borrowed("bar"));
assert_eq!(cow.as_str(), "bar");
let cow = StringCow::from_cow(std::borrow::Cow::Owned(String::from("baz")));
assert_eq!(cow.as_str(), "baz");
let cow = StringCow::from(std::borrow::Cow::Owned(String::from("qux")));
assert_eq!(cow.as_str(), "qux");
}
#[test]
fn into_bytes() {
let cow = StringCow::from_static("hello");
assert_eq!(cow.into_bytes(), ByteString::from_static("hello"));
let cow = StringCow::from_ref("world");
assert_eq!(cow.into_bytes(), ByteString::from_static("world"));
let cow = StringCow::from_string(String::from("foo"));
assert_eq!(cow.into_bytes(), ByteString::from_static("foo"));
let cow = StringCow::from_bytes(ByteString::from_static("foo"));
assert_eq!(cow.into_bytes(), ByteString::from_static("foo"));
let cow = StringCow::from_cow(std::borrow::Cow::Borrowed("bar"));
assert_eq!(cow.into_bytes(), ByteString::from_static("bar"));
let cow = StringCow::from_cow(std::borrow::Cow::Owned(String::from("baz")));
assert_eq!(cow.into_bytes(), ByteString::from_static("baz"));
}
#[test]
fn as_ref() {
let cow = StringCow::from_static("hello");
assert_eq!(cow.as_ref(), "hello");
let cow = StringCow::from_ref("world");
assert_eq!(cow.as_ref(), "world");
let cow = StringCow::from_string(String::from("foo"));
assert_eq!(cow.as_ref(), "foo");
let cow = StringCow::from_bytes(ByteString::from_static("foo"));
assert_eq!(cow.as_ref(), "foo");
}
#[test]
fn into_owned() {
let cow = StringCow::from_static("hello");
assert_eq!(cow.into_owned().as_str(), "hello");
let cow = StringCow::from_ref("world");
assert_eq!(cow.into_owned().as_str(), "world");
let cow = StringCow::from_string(String::from("foo"));
assert_eq!(cow.into_owned().as_str(), "foo");
let cow = StringCow::from_bytes(ByteString::from_static("foo"));
assert_eq!(cow.into_owned().as_str(), "foo");
}
#[test]
fn partial_eq() {
let cow = StringCow::from_static("hello");
assert!(cow == "hello");
assert!(cow != "world");
let cow = StringCow::from_ref("world");
assert!(cow == "world");
assert!(cow != "hello");
let cow = StringCow::from_string(String::from("foo"));
assert!(cow == "foo");
assert!(cow != "bar");
}
#[test]
fn hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
"hello".hash(&mut hasher);
let expected_hash = hasher.finish();
let cow = StringCow::from_static("hello");
let mut hasher = DefaultHasher::new();
cow.hash(&mut hasher);
assert_eq!(hasher.finish(), expected_hash);
}
#[test]
fn partial_ord() {
let cow1 = StringCow::from_static("hello");
let cow2 = StringCow::from_static("world");
assert!(cow1 < cow2);
let cow3 = StringCow::from_ref("foo");
let cow4 = StringCow::from_string(String::from("bar"));
assert!(cow3 > cow4);
}
#[test]
fn display() {
let cow = StringCow::from_ref("hello");
let fmt = format!("{cow}");
assert_eq!(fmt, "hello");
let cow = StringCow::from_static("hello");
let fmt = format!("{cow}");
assert_eq!(fmt, "hello");
let cow = StringCow::from_string(String::from("world"));
let fmt = format!("{cow}");
assert_eq!(fmt, "world");
let cow = StringCow::from_bytes(ByteString::from_static("foo"));
let fmt = format!("{cow}");
assert_eq!(fmt, "foo");
}
}