A better Rust ATProto crate
1use serde::{Deserialize, Serialize}; 2use smol_str::SmolStr; 3use std::{ 4 borrow::Cow, 5 fmt, 6 hash::{Hash, Hasher}, 7 ops::Deref, 8}; 9 10use crate::IntoStatic; 11 12/// Shamelessly copied from https://github.com/bearcove/merde 13/// A copy-on-write immutable string type that uses [`SmolStr`] for 14/// the "owned" variant. 15/// 16/// The standard [`Cow`] type cannot be used, since 17/// `<str as ToOwned>::Owned` is `String`, and not `SmolStr`. 18#[derive(Clone)] 19pub enum CowStr<'s> { 20 Borrowed(&'s str), 21 Owned(SmolStr), 22} 23 24impl CowStr<'static> { 25 /// Create a new `CowStr` by copying from a `&str` — this might allocate 26 /// if the `compact_str` feature is disabled, or if the string is longer 27 /// than `MAX_INLINE_SIZE`. 28 pub fn copy_from_str(s: &str) -> Self { 29 Self::Owned(SmolStr::from(s)) 30 } 31 32 pub fn new_static(s: &'static str) -> Self { 33 Self::Owned(SmolStr::new_static(s)) 34 } 35} 36 37impl<'s> CowStr<'s> { 38 #[inline] 39 pub fn from_utf8(s: &'s [u8]) -> Result<Self, std::str::Utf8Error> { 40 Ok(Self::Borrowed(std::str::from_utf8(s)?)) 41 } 42 43 #[inline] 44 pub fn from_utf8_owned(s: Vec<u8>) -> Result<Self, std::str::Utf8Error> { 45 Ok(Self::Owned(SmolStr::new(std::str::from_utf8(&s)?))) 46 } 47 48 #[inline] 49 pub fn from_utf8_lossy(s: &'s [u8]) -> Self { 50 Self::Owned(String::from_utf8_lossy(&s).into()) 51 } 52 53 /// # Safety 54 /// 55 /// This function is unsafe because it does not check that the bytes are valid UTF-8. 56 #[inline] 57 pub unsafe fn from_utf8_unchecked(s: &'s [u8]) -> Self { 58 unsafe { Self::Owned(SmolStr::new(std::str::from_utf8_unchecked(s))) } 59 } 60} 61 62impl AsRef<str> for CowStr<'_> { 63 #[inline] 64 fn as_ref(&self) -> &str { 65 match self { 66 CowStr::Borrowed(s) => s, 67 CowStr::Owned(s) => s.as_str(), 68 } 69 } 70} 71 72impl Deref for CowStr<'_> { 73 type Target = str; 74 75 #[inline] 76 fn deref(&self) -> &Self::Target { 77 match self { 78 CowStr::Borrowed(s) => s, 79 CowStr::Owned(s) => s.as_str(), 80 } 81 } 82} 83 84impl<'a> From<Cow<'a, str>> for CowStr<'a> { 85 #[inline] 86 fn from(s: Cow<'a, str>) -> Self { 87 match s { 88 Cow::Borrowed(s) => CowStr::Borrowed(s), 89 #[allow(clippy::useless_conversion)] 90 Cow::Owned(s) => CowStr::Owned(s.into()), 91 } 92 } 93} 94 95impl<'s> From<&'s str> for CowStr<'s> { 96 #[inline] 97 fn from(s: &'s str) -> Self { 98 CowStr::Borrowed(s) 99 } 100} 101 102impl From<String> for CowStr<'_> { 103 #[inline] 104 fn from(s: String) -> Self { 105 #[allow(clippy::useless_conversion)] 106 CowStr::Owned(s.into()) 107 } 108} 109 110impl From<Box<str>> for CowStr<'_> { 111 #[inline] 112 fn from(s: Box<str>) -> Self { 113 CowStr::Owned(s.into()) 114 } 115} 116 117impl<'s> From<&'s String> for CowStr<'s> { 118 #[inline] 119 fn from(s: &'s String) -> Self { 120 CowStr::Borrowed(s.as_str()) 121 } 122} 123 124impl From<CowStr<'_>> for String { 125 #[inline] 126 fn from(s: CowStr<'_>) -> Self { 127 match s { 128 CowStr::Borrowed(s) => s.into(), 129 #[allow(clippy::useless_conversion)] 130 CowStr::Owned(s) => s.into(), 131 } 132 } 133} 134 135impl From<CowStr<'_>> for Box<str> { 136 #[inline] 137 fn from(s: CowStr<'_>) -> Self { 138 match s { 139 CowStr::Borrowed(s) => s.into(), 140 CowStr::Owned(s) => String::from(s).into_boxed_str(), 141 } 142 } 143} 144 145impl<'a> PartialEq<CowStr<'a>> for CowStr<'_> { 146 #[inline] 147 fn eq(&self, other: &CowStr<'a>) -> bool { 148 self.deref() == other.deref() 149 } 150} 151 152impl PartialEq<&str> for CowStr<'_> { 153 #[inline] 154 fn eq(&self, other: &&str) -> bool { 155 self.deref() == *other 156 } 157} 158 159impl PartialEq<CowStr<'_>> for &str { 160 #[inline] 161 fn eq(&self, other: &CowStr<'_>) -> bool { 162 *self == other.deref() 163 } 164} 165 166impl PartialEq<String> for CowStr<'_> { 167 #[inline] 168 fn eq(&self, other: &String) -> bool { 169 self.deref() == other.as_str() 170 } 171} 172 173impl PartialEq<CowStr<'_>> for String { 174 #[inline] 175 fn eq(&self, other: &CowStr<'_>) -> bool { 176 self.as_str() == other.deref() 177 } 178} 179 180impl Eq for CowStr<'_> {} 181 182impl Hash for CowStr<'_> { 183 #[inline] 184 fn hash<H: Hasher>(&self, state: &mut H) { 185 self.deref().hash(state) 186 } 187} 188 189impl fmt::Debug for CowStr<'_> { 190 #[inline] 191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 192 self.deref().fmt(f) 193 } 194} 195 196impl fmt::Display for CowStr<'_> { 197 #[inline] 198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 199 self.deref().fmt(f) 200 } 201} 202 203impl IntoStatic for CowStr<'_> { 204 type Output = CowStr<'static>; 205 206 #[inline] 207 fn into_static(self) -> Self::Output { 208 match self { 209 CowStr::Borrowed(s) => CowStr::Owned((*s).into()), 210 CowStr::Owned(s) => CowStr::Owned(s), 211 } 212 } 213} 214 215impl Serialize for CowStr<'_> { 216 #[inline] 217 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 218 where 219 S: serde::Serializer, 220 { 221 serializer.serialize_str(self) 222 } 223} 224 225impl<'de: 'a, 'a> Deserialize<'de> for CowStr<'a> { 226 #[inline] 227 fn deserialize<D>(deserializer: D) -> Result<CowStr<'a>, D::Error> 228 where 229 D: serde::Deserializer<'de>, 230 { 231 struct CowStrVisitor; 232 233 impl<'de> serde::de::Visitor<'de> for CowStrVisitor { 234 type Value = CowStr<'de>; 235 236 #[inline] 237 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 238 write!(formatter, "a string") 239 } 240 241 #[inline] 242 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 243 where 244 E: serde::de::Error, 245 { 246 Ok(CowStr::copy_from_str(v)) 247 } 248 249 #[inline] 250 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> 251 where 252 E: serde::de::Error, 253 { 254 Ok(CowStr::Borrowed(v)) 255 } 256 257 #[inline] 258 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> 259 where 260 E: serde::de::Error, 261 { 262 Ok(v.into()) 263 } 264 } 265 266 deserializer.deserialize_str(CowStrVisitor) 267 } 268} 269 270#[cfg(test)] 271mod tests { 272 use super::*; 273 274 #[test] 275 fn test_partialeq_with_str() { 276 let cow_str1 = CowStr::Borrowed("hello"); 277 let cow_str2 = CowStr::Borrowed("hello"); 278 let cow_str3 = CowStr::Borrowed("world"); 279 280 assert_eq!(cow_str1, "hello"); 281 assert_eq!("hello", cow_str1); 282 assert_eq!(cow_str1, cow_str2); 283 assert_ne!(cow_str1, "world"); 284 assert_ne!("world", cow_str1); 285 assert_ne!(cow_str1, cow_str3); 286 } 287}