|
| 1 | +use super::*; |
| 2 | +use core::{cmp, fmt, hash, ops::Deref}; |
| 3 | +use std_alloc::borrow::Borrow; |
| 4 | + |
| 5 | +/// Like [`std::borrow::ToOwned`] but returns an [`OutOfMemory`] error on |
| 6 | +/// allocation failure. |
| 7 | +pub trait TryToOwned { |
| 8 | + /// The owned version of this type. |
| 9 | + type Owned: Borrow<Self>; |
| 10 | + |
| 11 | + /// Try to allocate an owned version of `self`. |
| 12 | + fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory>; |
| 13 | +} |
| 14 | + |
| 15 | +impl TryToOwned for str { |
| 16 | + type Owned = String; |
| 17 | + |
| 18 | + fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory> { |
| 19 | + let mut s = String::new(); |
| 20 | + s.push_str(self)?; |
| 21 | + Ok(s) |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +impl<T> TryToOwned for [T] |
| 26 | +where |
| 27 | + T: TryClone, |
| 28 | +{ |
| 29 | + type Owned = Vec<T>; |
| 30 | + |
| 31 | + fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory> { |
| 32 | + let mut v = Vec::with_capacity(self.len())?; |
| 33 | + for x in self { |
| 34 | + v.push(x.try_clone()?)?; |
| 35 | + } |
| 36 | + Ok(v) |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +impl<T> TryToOwned for T |
| 41 | +where |
| 42 | + T: TryClone, |
| 43 | +{ |
| 44 | + type Owned = Self; |
| 45 | + |
| 46 | + fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory> { |
| 47 | + self.try_clone() |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +/// Like [`std::borrow::Cow`] but returns [`OutOfMemory`] errors for various |
| 52 | +/// APIs that force allocation of an owned copy. |
| 53 | +pub enum TryCow<'a, B> |
| 54 | +where |
| 55 | + B: 'a + TryToOwned + ?Sized, |
| 56 | +{ |
| 57 | + /// Borrowed data. |
| 58 | + Borrowed(&'a B), |
| 59 | + |
| 60 | + /// Owned data. |
| 61 | + Owned(<B as TryToOwned>::Owned), |
| 62 | +} |
| 63 | + |
| 64 | +impl<'a, B> From<&'a B> for TryCow<'a, B> |
| 65 | +where |
| 66 | + B: 'a + ?Sized + TryToOwned, |
| 67 | +{ |
| 68 | + fn from(b: &'a B) -> Self { |
| 69 | + Self::Borrowed(b) |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +impl<B> Default for TryCow<'_, B> |
| 74 | +where |
| 75 | + B: ?Sized + TryToOwned<Owned: Default>, |
| 76 | +{ |
| 77 | + fn default() -> Self { |
| 78 | + Self::Owned(<B as TryToOwned>::Owned::default()) |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +impl<B> fmt::Debug for TryCow<'_, B> |
| 83 | +where |
| 84 | + B: ?Sized + fmt::Debug + TryToOwned<Owned: fmt::Debug>, |
| 85 | +{ |
| 86 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 87 | + match self { |
| 88 | + Self::Borrowed(b) => fmt::Debug::fmt(b, f), |
| 89 | + Self::Owned(o) => fmt::Debug::fmt(o, f), |
| 90 | + } |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +impl<B> TryClone for TryCow<'_, B> |
| 95 | +where |
| 96 | + B: ?Sized + TryToOwned, |
| 97 | +{ |
| 98 | + fn try_clone(&self) -> Result<Self, OutOfMemory> { |
| 99 | + match self { |
| 100 | + Self::Borrowed(b) => Ok(Self::Borrowed(b)), |
| 101 | + Self::Owned(o) => { |
| 102 | + let b: &B = o.borrow(); |
| 103 | + Ok(Self::Owned(b.try_to_owned()?)) |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +impl<B> Deref for TryCow<'_, B> |
| 110 | +where |
| 111 | + B: ?Sized + TryToOwned, |
| 112 | +{ |
| 113 | + type Target = B; |
| 114 | + |
| 115 | + fn deref(&self) -> &B { |
| 116 | + match self { |
| 117 | + Self::Borrowed(b) => b, |
| 118 | + Self::Owned(o) => o.borrow(), |
| 119 | + } |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +impl<B> AsRef<B> for TryCow<'_, B> |
| 124 | +where |
| 125 | + B: ?Sized + TryToOwned, |
| 126 | +{ |
| 127 | + fn as_ref(&self) -> &B { |
| 128 | + self |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +impl<'a, B> Borrow<B> for TryCow<'a, B> |
| 133 | +where |
| 134 | + B: ?Sized + TryToOwned, |
| 135 | +{ |
| 136 | + fn borrow(&self) -> &B { |
| 137 | + &**self |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +impl<B> hash::Hash for TryCow<'_, B> |
| 142 | +where |
| 143 | + B: ?Sized + hash::Hash + TryToOwned, |
| 144 | +{ |
| 145 | + fn hash<H>(&self, state: &mut H) |
| 146 | + where |
| 147 | + H: hash::Hasher, |
| 148 | + { |
| 149 | + hash::Hash::hash(&**self, state) |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +impl<B> PartialEq for TryCow<'_, B> |
| 154 | +where |
| 155 | + B: ?Sized + PartialEq + TryToOwned, |
| 156 | +{ |
| 157 | + fn eq(&self, other: &Self) -> bool { |
| 158 | + PartialEq::eq(&**self, &**other) |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 162 | +impl<B> Eq for TryCow<'_, B> where B: ?Sized + Eq + TryToOwned {} |
| 163 | + |
| 164 | +impl<B> PartialOrd for TryCow<'_, B> |
| 165 | +where |
| 166 | + B: ?Sized + PartialOrd + TryToOwned, |
| 167 | +{ |
| 168 | + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { |
| 169 | + PartialOrd::partial_cmp(&**self, &**other) |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +impl<B> Ord for TryCow<'_, B> |
| 174 | +where |
| 175 | + B: ?Sized + Ord + TryToOwned, |
| 176 | +{ |
| 177 | + fn cmp(&self, other: &Self) -> cmp::Ordering { |
| 178 | + Ord::cmp(&**self, &**other) |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +impl<'a, B> TryCow<'a, B> |
| 183 | +where |
| 184 | + B: TryToOwned + ?Sized, |
| 185 | +{ |
| 186 | + /// Same as [`std::borrow::Cow::to_mut`] but returns an [`OutOfMemory`] |
| 187 | + /// error on allocation failure. |
| 188 | + pub fn to_mut(&mut self) -> Result<&mut <B as TryToOwned>::Owned, OutOfMemory> { |
| 189 | + if let Self::Borrowed(b) = self { |
| 190 | + *self = Self::Owned(b.try_to_owned()?); |
| 191 | + } |
| 192 | + match self { |
| 193 | + TryCow::Owned(x) => Ok(x), |
| 194 | + TryCow::Borrowed(_) => unreachable!(), |
| 195 | + } |
| 196 | + } |
| 197 | + |
| 198 | + /// Same as [`std::borrow::Cow::into_owned`] but returns an [`OutOfMemory`] |
| 199 | + /// error on allocation failure. |
| 200 | + pub fn into_owned(self) -> Result<<B as TryToOwned>::Owned, OutOfMemory> { |
| 201 | + match self { |
| 202 | + TryCow::Borrowed(b) => b.try_to_owned(), |
| 203 | + TryCow::Owned(x) => Ok(x), |
| 204 | + } |
| 205 | + } |
| 206 | +} |
| 207 | + |
| 208 | +#[cfg(test)] |
| 209 | +mod tests { |
| 210 | + use super::*; |
| 211 | + use crate::error::Result; |
| 212 | + |
| 213 | + #[test] |
| 214 | + fn to_mut() -> Result<()> { |
| 215 | + let mut s = TryCow::Borrowed("hello"); |
| 216 | + s.to_mut()?.push_str(", world!")?; |
| 217 | + assert!(matches!(s, TryCow::Owned(_))); |
| 218 | + assert_eq!(&*s, "hello, world!"); |
| 219 | + Ok(()) |
| 220 | + } |
| 221 | + |
| 222 | + #[test] |
| 223 | + fn into_owned() -> Result<()> { |
| 224 | + let v = TryCow::Borrowed(&[42u8, 36][..]); |
| 225 | + let v: Vec<u8> = v.into_owned()?; |
| 226 | + assert_eq!(&*v, &[42, 36]); |
| 227 | + Ok(()) |
| 228 | + } |
| 229 | +} |
0 commit comments