A better Rust ATProto crate
103
fork

Configure Feed

Select the types of activity you want to include in your feed.

at pretty-codegen 369 lines 9.0 kB view raw
1use alloc::borrow::Cow; 2use alloc::boxed::Box; 3use alloc::string::String; 4use core::fmt; 5use core::hash::{Hash, Hasher}; 6use core::ops::Deref; 7 8use serde::{Deserialize, Serialize}; 9use smol_str::SmolStr; 10 11use crate::IntoStatic; 12 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/// 19/// Shamelessly ported from [merde](https://github.com/bearcove/merde) 20#[derive(Clone)] 21pub enum CowStr<'s> { 22 /// &str varaiant 23 Borrowed(&'s str), 24 /// Smolstr variant 25 Owned(SmolStr), 26} 27 28impl CowStr<'static> { 29 /// Create a new `CowStr` by copying from a `&str` — this might allocate 30 /// if the string is longer than `MAX_INLINE_SIZE`. 31 pub fn copy_from_str(s: &str) -> Self { 32 Self::Owned(SmolStr::from(s)) 33 } 34 35 /// Create a new owned `CowStr` from a static &str without allocating 36 pub fn new_static(s: &'static str) -> Self { 37 Self::Owned(SmolStr::new_static(s)) 38 } 39} 40 41impl<'s> CowStr<'s> { 42 #[inline] 43 /// Borrow and decode a byte slice as utf8 into a CowStr 44 pub fn from_utf8(s: &'s [u8]) -> Result<Self, core::str::Utf8Error> { 45 Ok(Self::Borrowed(core::str::from_utf8(s)?)) 46 } 47 48 #[inline] 49 /// Take bytes and decode them as utf8 into an owned CowStr. Might allocate. 50 pub fn from_utf8_owned(s: impl AsRef<[u8]>) -> Result<Self, core::str::Utf8Error> { 51 Ok(Self::Owned(SmolStr::new(core::str::from_utf8(s.as_ref())?))) 52 } 53 54 #[inline] 55 /// Take bytes and decode them as utf8, skipping invalid characters, taking ownership. 56 /// Will allocate, uses String::from_utf8_lossy() internally for now. 57 pub fn from_utf8_lossy(s: &'s [u8]) -> Self { 58 Self::Owned(String::from_utf8_lossy(&s).into()) 59 } 60 61 /// # Safety 62 /// 63 /// This function is unsafe because it does not check that the bytes are valid UTF-8. 64 #[inline] 65 pub unsafe fn from_utf8_unchecked(s: &'s [u8]) -> Self { 66 unsafe { Self::Owned(SmolStr::new(core::str::from_utf8_unchecked(s))) } 67 } 68 69 /// Returns a reference to the underlying string slice. 70 #[inline] 71 pub fn as_str(&self) -> &str { 72 match self { 73 CowStr::Borrowed(s) => s, 74 CowStr::Owned(s) => s.as_str(), 75 } 76 } 77} 78 79impl AsRef<str> for CowStr<'_> { 80 #[inline] 81 fn as_ref(&self) -> &str { 82 match self { 83 CowStr::Borrowed(s) => s, 84 CowStr::Owned(s) => s.as_str(), 85 } 86 } 87} 88 89impl Deref for CowStr<'_> { 90 type Target = str; 91 92 #[inline] 93 fn deref(&self) -> &Self::Target { 94 match self { 95 CowStr::Borrowed(s) => s, 96 CowStr::Owned(s) => s.as_str(), 97 } 98 } 99} 100 101impl<'a> From<Cow<'a, str>> for CowStr<'a> { 102 #[inline] 103 fn from(s: Cow<'a, str>) -> Self { 104 match s { 105 Cow::Borrowed(s) => CowStr::Borrowed(s), 106 #[allow(clippy::useless_conversion)] 107 Cow::Owned(s) => CowStr::Owned(s.into()), 108 } 109 } 110} 111 112impl<'s> From<&'s str> for CowStr<'s> { 113 #[inline] 114 fn from(s: &'s str) -> Self { 115 CowStr::Borrowed(s) 116 } 117} 118 119impl Default for CowStr<'_> { 120 #[inline] 121 fn default() -> Self { 122 CowStr::new_static("") 123 } 124} 125 126impl From<String> for CowStr<'_> { 127 #[inline] 128 fn from(s: String) -> Self { 129 #[allow(clippy::useless_conversion)] 130 CowStr::Owned(s.into()) 131 } 132} 133 134impl From<Box<str>> for CowStr<'_> { 135 #[inline] 136 fn from(s: Box<str>) -> Self { 137 CowStr::Owned(s.into()) 138 } 139} 140 141impl<'s> From<&'s String> for CowStr<'s> { 142 #[inline] 143 fn from(s: &'s String) -> Self { 144 CowStr::Borrowed(s.as_str()) 145 } 146} 147 148impl From<CowStr<'_>> for String { 149 #[inline] 150 fn from(s: CowStr<'_>) -> Self { 151 match s { 152 CowStr::Borrowed(s) => s.into(), 153 #[allow(clippy::useless_conversion)] 154 CowStr::Owned(s) => s.into(), 155 } 156 } 157} 158 159impl From<CowStr<'_>> for SmolStr { 160 #[inline] 161 fn from(s: CowStr<'_>) -> Self { 162 match s { 163 CowStr::Borrowed(s) => SmolStr::new(s), 164 CowStr::Owned(s) => SmolStr::new(s), 165 } 166 } 167} 168 169impl From<SmolStr> for CowStr<'_> { 170 #[inline] 171 fn from(s: SmolStr) -> Self { 172 CowStr::Owned(s) 173 } 174} 175 176impl From<CowStr<'_>> for Box<str> { 177 #[inline] 178 fn from(s: CowStr<'_>) -> Self { 179 match s { 180 CowStr::Borrowed(s) => s.into(), 181 CowStr::Owned(s) => String::from(s).into_boxed_str(), 182 } 183 } 184} 185 186impl<'a> PartialEq<CowStr<'a>> for CowStr<'_> { 187 #[inline] 188 fn eq(&self, other: &CowStr<'a>) -> bool { 189 self.deref() == other.deref() 190 } 191} 192 193impl PartialEq<&str> for CowStr<'_> { 194 #[inline] 195 fn eq(&self, other: &&str) -> bool { 196 self.deref() == *other 197 } 198} 199 200impl PartialEq<CowStr<'_>> for &str { 201 #[inline] 202 fn eq(&self, other: &CowStr<'_>) -> bool { 203 *self == other.deref() 204 } 205} 206 207impl PartialEq<String> for CowStr<'_> { 208 #[inline] 209 fn eq(&self, other: &String) -> bool { 210 self.deref() == other.as_str() 211 } 212} 213 214impl PartialEq<CowStr<'_>> for String { 215 #[inline] 216 fn eq(&self, other: &CowStr<'_>) -> bool { 217 self.as_str() == other.deref() 218 } 219} 220 221impl PartialOrd for CowStr<'_> { 222 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { 223 Some(match (self, other) { 224 (CowStr::Borrowed(s1), CowStr::Borrowed(s2)) => s1.cmp(s2), 225 (CowStr::Borrowed(s1), CowStr::Owned(s2)) => s1.cmp(&s2.as_ref()), 226 (CowStr::Owned(s1), CowStr::Borrowed(s2)) => s1.as_str().cmp(s2), 227 (CowStr::Owned(s1), CowStr::Owned(s2)) => s1.cmp(s2), 228 }) 229 } 230} 231 232impl Ord for CowStr<'_> { 233 fn cmp(&self, other: &Self) -> core::cmp::Ordering { 234 match (self, other) { 235 (CowStr::Borrowed(s1), CowStr::Borrowed(s2)) => s1.cmp(s2), 236 (CowStr::Borrowed(s1), CowStr::Owned(s2)) => s1.cmp(&s2.as_ref()), 237 (CowStr::Owned(s1), CowStr::Borrowed(s2)) => s1.as_str().cmp(s2), 238 (CowStr::Owned(s1), CowStr::Owned(s2)) => s1.cmp(s2), 239 } 240 } 241} 242 243impl Eq for CowStr<'_> {} 244 245impl Hash for CowStr<'_> { 246 #[inline] 247 fn hash<H: Hasher>(&self, state: &mut H) { 248 self.deref().hash(state) 249 } 250} 251 252impl fmt::Debug for CowStr<'_> { 253 #[inline] 254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 255 self.deref().fmt(f) 256 } 257} 258 259impl fmt::Display for CowStr<'_> { 260 #[inline] 261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 262 self.deref().fmt(f) 263 } 264} 265 266impl IntoStatic for CowStr<'_> { 267 type Output = CowStr<'static>; 268 269 #[inline] 270 fn into_static(self) -> Self::Output { 271 match self { 272 CowStr::Borrowed(s) => CowStr::Owned((*s).into()), 273 CowStr::Owned(s) => CowStr::Owned(s), 274 } 275 } 276} 277 278impl Serialize for CowStr<'_> { 279 #[inline] 280 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 281 where 282 S: serde::Serializer, 283 { 284 serializer.serialize_str(self) 285 } 286} 287 288/// Deserialization helper for things that wrap a CowStr 289pub struct CowStrVisitor; 290 291impl<'de> serde::de::Visitor<'de> for CowStrVisitor { 292 type Value = CowStr<'de>; 293 294 #[inline] 295 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 296 write!(formatter, "a string") 297 } 298 299 #[inline] 300 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 301 where 302 E: serde::de::Error, 303 { 304 Ok(CowStr::copy_from_str(v)) 305 } 306 307 #[inline] 308 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> 309 where 310 E: serde::de::Error, 311 { 312 Ok(CowStr::Borrowed(v)) 313 } 314 315 #[inline] 316 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> 317 where 318 E: serde::de::Error, 319 { 320 Ok(v.into()) 321 } 322} 323 324impl<'de, 'a> Deserialize<'de> for CowStr<'a> 325where 326 'de: 'a, 327{ 328 #[inline] 329 fn deserialize<D>(deserializer: D) -> Result<CowStr<'a>, D::Error> 330 where 331 D: serde::Deserializer<'de>, 332 { 333 deserializer.deserialize_str(CowStrVisitor) 334 } 335} 336 337/// Convert to a CowStr. 338pub trait ToCowStr { 339 /// Convert to a CowStr. 340 fn to_cowstr(&self) -> CowStr<'_>; 341} 342 343impl<T> ToCowStr for T 344where 345 T: fmt::Display + ?Sized, 346{ 347 fn to_cowstr(&self) -> CowStr<'_> { 348 CowStr::Owned(smol_str::format_smolstr!("{}", self)) 349 } 350} 351 352#[cfg(test)] 353mod tests { 354 use super::*; 355 356 #[test] 357 fn test_partialeq_with_str() { 358 let cow_str1 = CowStr::Borrowed("hello"); 359 let cow_str2 = CowStr::Borrowed("hello"); 360 let cow_str3 = CowStr::Borrowed("world"); 361 362 assert_eq!(cow_str1, "hello"); 363 assert_eq!("hello", cow_str1); 364 assert_eq!(cow_str1, cow_str2); 365 assert_ne!(cow_str1, "world"); 366 assert_ne!("world", cow_str1); 367 assert_ne!(cow_str1, cow_str3); 368 } 369}