Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 1 | // Protocol Buffers - Google's data interchange format |
Hong Shin | 4f20efb | 2023-07-10 07:35:36 -0700 | [diff] [blame] | 2 | // Copyright 2023 Google LLC. All rights reserved. |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 3 | // |
Joshua Haberman | 4a51303 | 2023-09-08 17:12:50 -0700 | [diff] [blame] | 4 | // Use of this source code is governed by a BSD-style |
| 5 | // license that can be found in the LICENSE file or at |
| 6 | // https://developers.google.com/open-source/licenses/bsd |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 7 | |
| 8 | //! Items specific to `optional` fields. |
| 9 | #![allow(dead_code)] |
| 10 | #![allow(unused)] |
| 11 | |
Protobuf Team Bot | 29a2615 | 2023-06-22 09:51:38 -0700 | [diff] [blame] | 12 | use crate::__internal::Private; |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 13 | use crate::{Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy}; |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 14 | use std::convert::{AsMut, AsRef}; |
| 15 | use std::fmt::{self, Debug}; |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 16 | use std::panic; |
| 17 | use std::ptr; |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 18 | |
| 19 | /// A protobuf value from a field that may not be set. |
| 20 | /// |
| 21 | /// This can be pattern matched with `match` or `if let` to determine if the |
| 22 | /// field is set and access the field data. |
| 23 | /// |
| 24 | /// [`FieldEntry`], a specific type alias for `Optional`, provides much of the |
| 25 | /// functionality for this type. |
| 26 | /// |
| 27 | /// Two `Optional`s are equal if they match both presence and the field values. |
| 28 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 29 | pub enum Optional<SetVal, UnsetVal = SetVal> { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 30 | /// The field is set; it is present in the serialized message. |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 31 | /// |
| 32 | /// - For an `_opt()` accessor, this contains a `View<impl Proxied>`. |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 33 | /// - For a `_mut()` accessor, this contains a [`PresentField`] that can be |
| 34 | /// used to access the current value, convert to [`Mut`], clear presence, |
| 35 | /// or set a new value. |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 36 | Set(SetVal), |
| 37 | |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 38 | /// The field is unset; it is absent in the serialized message. |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 39 | /// |
| 40 | /// - For an `_opt()` accessor, this contains a `View<impl Proxied>` with |
| 41 | /// the default value. |
| 42 | /// - For a `_mut()` accessor, this contains an [`AbsentField`] that can be |
| 43 | /// used to access the default or set a new value. |
| 44 | Unset(UnsetVal), |
| 45 | } |
| 46 | |
| 47 | impl<T> Optional<T> { |
| 48 | /// Gets the field value, ignoring whether it was set or not. |
| 49 | pub fn into_inner(self) -> T { |
| 50 | match self { |
| 51 | Optional::Set(x) | Optional::Unset(x) => x, |
| 52 | } |
| 53 | } |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 54 | |
| 55 | /// Constructs an `Optional<T>` with a `T` value and presence bit. |
| 56 | pub fn new(val: T, is_set: bool) -> Self { |
| 57 | if is_set { Optional::Set(val) } else { Optional::Unset(val) } |
| 58 | } |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 59 | } |
| 60 | |
| 61 | impl<T, A> Optional<T, A> { |
| 62 | /// Converts into an `Option` of the set value, ignoring any unset value. |
| 63 | pub fn into_option(self) -> Option<T> { |
| 64 | if let Optional::Set(x) = self { Some(x) } else { None } |
| 65 | } |
| 66 | |
| 67 | /// Returns if the field is set. |
| 68 | pub fn is_set(&self) -> bool { |
| 69 | matches!(self, Optional::Set(_)) |
| 70 | } |
| 71 | |
| 72 | /// Returns if the field is unset. |
| 73 | pub fn is_unset(&self) -> bool { |
| 74 | matches!(self, Optional::Unset(_)) |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | impl<T> From<Optional<T>> for Option<T> { |
| 79 | fn from(x: Optional<T>) -> Option<T> { |
| 80 | x.into_option() |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | /// A mutable view into the value of an optional field, which may be set or |
| 85 | /// unset. |
| 86 | pub type FieldEntry<'a, T> = Optional<PresentField<'a, T>, AbsentField<'a, T>>; |
| 87 | |
| 88 | /// Methods for `_mut()` accessors of optional types. |
| 89 | /// |
| 90 | /// The most common methods are [`set`] and [`or_default`]. |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 91 | impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> FieldEntry<'msg, T> { |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 92 | // is_set() is provided by `impl<T, A> Optional<T, A>` |
| 93 | |
| 94 | /// Gets a mutator for this field. Sets to the default value if not set. |
| 95 | pub fn or_default(self) -> Mut<'msg, T> { |
| 96 | match self { |
| 97 | Optional::Set(x) => x.into_mut(), |
| 98 | Optional::Unset(x) => x.set_default().into_mut(), |
| 99 | } |
| 100 | } |
| 101 | |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 102 | /// Gets a mutator for this field. Sets to the given `val` if not set. |
| 103 | /// |
| 104 | /// If the field is already set, `val` is ignored. |
| 105 | pub fn or_set(self, val: impl SettableValue<T>) -> Mut<'msg, T> { |
| 106 | self.or_set_with(move || val) |
| 107 | } |
| 108 | |
| 109 | /// Gets a mutator for this field. Sets using the given `val` function if |
| 110 | /// not set. |
| 111 | /// |
| 112 | /// If the field is already set, `val` is not invoked. |
| 113 | pub fn or_set_with<S>(self, val: impl FnOnce() -> S) -> Mut<'msg, T> |
| 114 | where |
| 115 | S: SettableValue<T>, |
| 116 | { |
| 117 | match self { |
| 118 | Optional::Set(x) => x.into_mut(), |
| 119 | Optional::Unset(x) => x.set(val()).into_mut(), |
| 120 | } |
| 121 | } |
| 122 | |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 123 | /// Sets the value of this field to `val`. |
| 124 | /// |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 125 | /// Equivalent to `self.or_default().set(val)`, but does not consume `self`. |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 126 | /// |
| 127 | /// `set` has the same parameters as in [`MutProxy`], so making a field |
| 128 | /// `optional` will switch to using this method. This makes transitioning |
| 129 | /// from implicit to explicit presence easier. |
Protobuf Team Bot | 29a2615 | 2023-06-22 09:51:38 -0700 | [diff] [blame] | 130 | pub fn set(&mut self, val: impl SettableValue<T>) { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 131 | transform_mut(self, |mut self_| match self_ { |
| 132 | Optional::Set(ref mut present) => { |
| 133 | present.set(val); |
| 134 | self_ |
| 135 | } |
| 136 | Optional::Unset(absent) => Optional::Set(absent.set(val)), |
| 137 | }) |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | /// Clears the field; `is_set()` will return `false`. |
| 141 | pub fn clear(&mut self) { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 142 | transform_mut(self, |self_| match self_ { |
| 143 | Optional::Set(present) => Optional::Unset(present.clear()), |
| 144 | absent => absent, |
| 145 | }) |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 146 | } |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 147 | |
| 148 | /// Gets an immutable view of this field, using its default value if not |
| 149 | /// set. This is shorthand for `as_view`. |
| 150 | /// |
| 151 | /// This provides a shorter lifetime than `into_view` but can also be called |
| 152 | /// multiple times - if the result of `get` is not living long enough |
| 153 | /// for your use, use that instead. |
| 154 | /// |
| 155 | /// `get` has the same parameters as in [`MutProxy`], so making a field |
| 156 | /// `optional` will switch to using this method. This makes transitioning |
| 157 | /// from implicit to explicit presence easier. |
| 158 | pub fn get(&self) -> View<'_, T> { |
| 159 | self.as_view() |
| 160 | } |
| 161 | |
| 162 | /// Converts to an immutable view of this optional field, preserving the |
| 163 | /// field's presence. |
| 164 | pub fn into_optional_view(self) -> Optional<View<'msg, T>> { |
| 165 | let is_set = self.is_set(); |
| 166 | Optional::new(self.into_view(), is_set) |
| 167 | } |
| 168 | |
| 169 | /// Returns a field mutator if the field is set. |
| 170 | /// |
| 171 | /// Returns `None` if the field is not set. This does not affect `is_set()`. |
| 172 | /// |
| 173 | /// This returns `Option` and _not_ `Optional` since returning a defaulted |
| 174 | /// `Mut` would require mutating the presence of the field - for that |
| 175 | /// behavior, use `or_default()`. |
| 176 | pub fn try_into_mut(self) -> Option<Mut<'msg, T>> { |
| 177 | match self { |
| 178 | Optional::Set(x) => Some(x.into_mut()), |
| 179 | Optional::Unset(_) => None, |
| 180 | } |
| 181 | } |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 182 | } |
| 183 | |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 184 | impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> ViewProxy<'msg> for FieldEntry<'msg, T> { |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 185 | type Proxied = T; |
| 186 | |
| 187 | fn as_view(&self) -> View<'_, T> { |
| 188 | match self { |
| 189 | Optional::Set(x) => x.as_view(), |
| 190 | Optional::Unset(x) => x.as_view(), |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | fn into_view<'shorter>(self) -> View<'shorter, T> |
| 195 | where |
| 196 | 'msg: 'shorter, |
| 197 | { |
| 198 | match self { |
| 199 | Optional::Set(x) => x.into_view(), |
| 200 | Optional::Unset(x) => x.into_view(), |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | // `MutProxy` not implemented for `FieldEntry` since the field may not be set, |
| 206 | // and `as_mut`/`into_mut` should not insert. |
| 207 | |
| 208 | /// A field mutator capable of clearing that is statically known to point to a |
| 209 | /// set field. |
| 210 | pub struct PresentField<'msg, T> |
| 211 | where |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 212 | T: ProxiedWithPresence + ?Sized + 'msg, |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 213 | { |
Alyssa Haroldsen | 614e29f | 2023-08-30 12:51:55 -0700 | [diff] [blame] | 214 | pub(crate) inner: T::PresentMutData<'msg>, |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 215 | } |
| 216 | |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 217 | impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> Debug for PresentField<'msg, T> { |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 218 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 219 | self.inner.fmt(f) |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 220 | } |
| 221 | } |
| 222 | |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 223 | impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> PresentField<'msg, T> { |
| 224 | #[doc(hidden)] |
| 225 | pub fn from_inner(_private: Private, inner: T::PresentMutData<'msg>) -> Self { |
| 226 | Self { inner } |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 227 | } |
| 228 | |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 229 | /// Gets an immutable view of this present field. This is shorthand for |
| 230 | /// `as_view`. |
| 231 | /// |
| 232 | /// This provides a shorter lifetime than `into_view` but can also be called |
| 233 | /// multiple times - if the result of `get` is not living long enough |
| 234 | /// for your use, use that instead. |
| 235 | pub fn get(&self) -> View<'_, T> { |
| 236 | self.as_view() |
| 237 | } |
| 238 | |
Protobuf Team Bot | 29a2615 | 2023-06-22 09:51:38 -0700 | [diff] [blame] | 239 | pub fn set(&mut self, val: impl SettableValue<T>) { |
| 240 | val.set_on(Private, self.as_mut()) |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 241 | } |
| 242 | |
| 243 | /// See [`FieldEntry::clear`]. |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 244 | pub fn clear(mut self) -> AbsentField<'msg, T> { |
| 245 | AbsentField { inner: T::clear_present_field(self.inner) } |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | // This cannot provide `reborrow` - `clear` consumes after setting the field |
| 249 | // because it would violate a condition of `PresentField` - the field being set. |
| 250 | } |
| 251 | |
| 252 | impl<'msg, T> ViewProxy<'msg> for PresentField<'msg, T> |
| 253 | where |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 254 | T: ProxiedWithPresence + ?Sized + 'msg, |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 255 | { |
| 256 | type Proxied = T; |
| 257 | |
| 258 | fn as_view(&self) -> View<'_, T> { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 259 | self.inner.as_view() |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | fn into_view<'shorter>(self) -> View<'shorter, T> |
| 263 | where |
| 264 | 'msg: 'shorter, |
| 265 | { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 266 | self.inner.into_view() |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 267 | } |
| 268 | } |
| 269 | |
| 270 | impl<'msg, T> MutProxy<'msg> for PresentField<'msg, T> |
| 271 | where |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 272 | T: ProxiedWithPresence + ?Sized + 'msg, |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 273 | { |
| 274 | fn as_mut(&mut self) -> Mut<'_, T> { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 275 | self.inner.as_mut() |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 276 | } |
| 277 | |
| 278 | fn into_mut<'shorter>(self) -> Mut<'shorter, T> |
| 279 | where |
| 280 | 'msg: 'shorter, |
| 281 | { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 282 | self.inner.into_mut() |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 283 | } |
| 284 | } |
| 285 | |
| 286 | /// A field mutator capable of setting that is statically known to point to a |
| 287 | /// non-set field. |
| 288 | pub struct AbsentField<'a, T> |
| 289 | where |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 290 | T: ProxiedWithPresence + ?Sized + 'a, |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 291 | { |
Alyssa Haroldsen | 614e29f | 2023-08-30 12:51:55 -0700 | [diff] [blame] | 292 | pub(crate) inner: T::AbsentMutData<'a>, |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 293 | } |
| 294 | |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 295 | impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> Debug for AbsentField<'msg, T> { |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 296 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 297 | self.inner.fmt(f) |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 298 | } |
| 299 | } |
| 300 | |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 301 | impl<'msg, T: ProxiedWithPresence + ?Sized> AbsentField<'msg, T> { |
| 302 | #[doc(hidden)] |
| 303 | pub fn from_inner(_private: Private, inner: T::AbsentMutData<'msg>) -> Self { |
| 304 | Self { inner } |
| 305 | } |
| 306 | |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 307 | /// Gets the default value for this unset field. |
| 308 | /// |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 309 | /// This is the same value that the primitive accessor would provide, though |
| 310 | /// with the shorter lifetime of `as_view`. |
| 311 | pub fn default_value(&self) -> View<'_, T> { |
| 312 | self.as_view() |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 313 | } |
| 314 | |
| 315 | /// See [`FieldEntry::set`]. Note that this consumes and returns a |
| 316 | /// `PresentField`. |
Protobuf Team Bot | 29a2615 | 2023-06-22 09:51:38 -0700 | [diff] [blame] | 317 | pub fn set(self, val: impl SettableValue<T>) -> PresentField<'msg, T> { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 318 | PresentField { inner: val.set_on_absent(Private, self.inner) } |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 319 | } |
| 320 | |
| 321 | /// Sets this absent field to its default value. |
| 322 | pub fn set_default(self) -> PresentField<'msg, T> { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 323 | PresentField { inner: T::set_absent_to_default(self.inner) } |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 324 | } |
| 325 | |
| 326 | // This cannot provide `reborrow` - `set` consumes after setting the field |
| 327 | // because it would violate a condition of `AbsentField` - the field being |
| 328 | // unset. |
| 329 | } |
| 330 | |
| 331 | impl<'msg, T> ViewProxy<'msg> for AbsentField<'msg, T> |
| 332 | where |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 333 | T: ProxiedWithPresence + ?Sized + 'msg, |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 334 | { |
| 335 | type Proxied = T; |
| 336 | |
| 337 | fn as_view(&self) -> View<'_, T> { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 338 | self.inner.as_view() |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 339 | } |
| 340 | |
| 341 | fn into_view<'shorter>(self) -> View<'shorter, T> |
| 342 | where |
| 343 | 'msg: 'shorter, |
| 344 | { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 345 | self.inner.into_view() |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | /// Transforms a mutable reference in-place, treating it as if it were owned. |
| 350 | /// |
| 351 | /// The program will abort if `transform` panics. |
| 352 | /// |
| 353 | /// This is the same operation as provided by [`take_mut::take`]. |
| 354 | /// |
| 355 | /// [`take_mut::take`]: https://docs.rs/take_mut/latest/take_mut/fn.take.html |
| 356 | fn transform_mut<T>(mut_ref: &mut T, transform: impl FnOnce(T) -> T) { |
| 357 | #[cold] |
| 358 | #[inline(never)] |
| 359 | fn panicked_in_transform_mut() -> ! { |
| 360 | use std::io::Write as _; |
| 361 | let backtrace = std::backtrace::Backtrace::force_capture(); |
| 362 | let stderr = std::io::stderr(); |
| 363 | let mut stderr = stderr.lock(); |
| 364 | let _ = write!(&mut stderr, "BUG: A protobuf mutator panicked! Backtrace:\n{backtrace}\n"); |
| 365 | let _ = stderr.flush(); |
| 366 | std::process::abort() |
| 367 | } |
| 368 | |
| 369 | // https://play.rust-lang.org/?edition=2021&gist=f3014e1f209013f0a38352e211f4a240 |
| 370 | // provides a sample test to confirm this operation is sound in Miri. |
| 371 | // SAFETY: |
| 372 | // - `old_t` is not dropped without also replacing `*mut_ref`, preventing a |
| 373 | // double-free. |
| 374 | // - If `transform` panics, the process aborts since `*mut_ref` has no possible |
| 375 | // valid value. |
| 376 | // - After `ptr::write`, a valid `T` is located at `*mut_ref` |
| 377 | unsafe { |
| 378 | let p: *mut T = mut_ref; |
| 379 | let old_t = p.read(); |
| 380 | let new_t = panic::catch_unwind(panic::AssertUnwindSafe(move || transform(old_t))) |
| 381 | .unwrap_or_else(|_| panicked_in_transform_mut()); |
| 382 | p.write(new_t); |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | #[cfg(test)] |
| 387 | mod tests { |
| 388 | use super::*; |
| 389 | use std::borrow::Cow; |
| 390 | |
| 391 | /// A sample message with custom presence bits, meant to mirror a C++ |
| 392 | /// message. |
| 393 | #[derive(Default, Debug)] |
| 394 | struct MyMessage { |
| 395 | /// has a default of `0` |
| 396 | a: i32, |
| 397 | |
| 398 | /// has a default of `5` |
| 399 | b: i32, |
| 400 | |
| 401 | /// Packed presence bitfield for `a` and `b` |
| 402 | presence: u8, |
| 403 | } |
| 404 | |
| 405 | impl MyMessage { |
| 406 | fn a(&self) -> View<'_, VtableProxied> { |
| 407 | VtableProxiedView { val: get_a(self) } |
| 408 | } |
| 409 | |
| 410 | fn a_opt(&self) -> Optional<View<'_, VtableProxied>> { |
| 411 | Optional::new(self.a(), has_a(self)) |
| 412 | } |
| 413 | |
| 414 | fn a_mut(&mut self) -> FieldEntry<'_, VtableProxied> { |
| 415 | static A_VTABLE: ProxyVtable = |
| 416 | ProxyVtable { get: get_a, set: set_a, clear: clear_a, has: has_a }; |
| 417 | make_field_entry(self, &A_VTABLE) |
| 418 | } |
| 419 | |
| 420 | fn b(&self) -> View<'_, VtableProxied> { |
| 421 | VtableProxiedView { val: get_b(self) } |
| 422 | } |
| 423 | |
| 424 | fn b_opt(&self) -> Optional<View<'_, VtableProxied>> { |
| 425 | Optional::new(self.b(), has_b(self)) |
| 426 | } |
| 427 | |
| 428 | fn b_mut(&mut self) -> FieldEntry<'_, VtableProxied> { |
| 429 | static B_VTABLE: ProxyVtable = |
| 430 | ProxyVtable { get: get_b, set: set_b, clear: clear_b, has: has_b }; |
| 431 | make_field_entry(self, &B_VTABLE) |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | fn make_field_entry<'a>( |
| 436 | msg: &'a mut MyMessage, |
| 437 | vtable: &'a ProxyVtable, |
| 438 | ) -> FieldEntry<'a, VtableProxied> { |
| 439 | if (vtable.has)(&*msg) { |
| 440 | Optional::Set(PresentField::from_inner(Private, VtableProxiedMut { msg, vtable })) |
| 441 | } else { |
| 442 | Optional::Unset(AbsentField::from_inner(Private, VtableProxiedMut { msg, vtable })) |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | // Thunks used for the vtable. For a C++ message these would be defined in C++ |
| 447 | // and exported via a C API |
| 448 | const A_BIT: u8 = 0; |
| 449 | const B_BIT: u8 = 1; |
| 450 | |
| 451 | fn get_a(msg: &MyMessage) -> i32 { |
| 452 | if has_a(msg) { msg.a } else { 0 } |
| 453 | } |
| 454 | |
| 455 | fn get_b(msg: &MyMessage) -> i32 { |
| 456 | if has_b(msg) { msg.b } else { 5 } |
| 457 | } |
| 458 | |
| 459 | fn set_a(msg: &mut MyMessage, val: i32) { |
| 460 | msg.presence |= (1 << A_BIT); |
| 461 | msg.a = val; |
| 462 | } |
| 463 | |
| 464 | fn set_b(msg: &mut MyMessage, val: i32) { |
| 465 | msg.presence |= (1 << B_BIT); |
| 466 | msg.b = val; |
| 467 | } |
| 468 | |
| 469 | fn clear_a(msg: &mut MyMessage) { |
| 470 | msg.presence &= !(1 << A_BIT); |
| 471 | } |
| 472 | |
| 473 | fn clear_b(msg: &mut MyMessage) { |
| 474 | msg.presence &= !(1 << B_BIT); |
| 475 | } |
| 476 | |
| 477 | fn has_a(msg: &MyMessage) -> bool { |
| 478 | msg.presence & (1 << A_BIT) != 0 |
| 479 | } |
| 480 | |
| 481 | fn has_b(msg: &MyMessage) -> bool { |
| 482 | msg.presence & (1 << B_BIT) != 0 |
| 483 | } |
| 484 | |
Marcel Hlopko | 8e210a8 | 2023-07-18 10:14:31 -0700 | [diff] [blame] | 485 | #[derive(Debug)] |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 486 | struct ProxyVtable { |
| 487 | get: fn(&MyMessage) -> i32, |
| 488 | set: fn(&mut MyMessage, val: i32), |
| 489 | clear: fn(&mut MyMessage), |
| 490 | has: fn(&MyMessage) -> bool, |
| 491 | } |
| 492 | |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 493 | /// A proxy for a `i32` that is accessed through methods on a vtable. |
| 494 | struct VtableProxied; |
| 495 | |
| 496 | impl Proxied for VtableProxied { |
| 497 | type View<'a> = VtableProxiedView; |
| 498 | type Mut<'a> = VtableProxiedMut<'a>; |
| 499 | } |
| 500 | |
| 501 | impl ProxiedWithPresence for VtableProxied { |
| 502 | // In this case, the `PresentMutData` and `AbsentMutData` are identical to the |
| 503 | // `Mut` in layout. Other types/runtimes could require otherwise, e.g. `Mut` |
| 504 | // could be defined to only have get/set functions in its vtable, and not |
| 505 | // has/clear. |
| 506 | type PresentMutData<'a> = VtableProxiedMut<'a>; |
| 507 | type AbsentMutData<'a> = VtableProxiedMut<'a>; |
| 508 | |
| 509 | fn clear_present_field<'a>( |
| 510 | present_mutator: Self::PresentMutData<'a>, |
| 511 | ) -> Self::AbsentMutData<'a> { |
| 512 | (present_mutator.vtable.clear)(&mut *present_mutator.msg); |
| 513 | present_mutator |
| 514 | } |
| 515 | |
| 516 | fn set_absent_to_default<'a>( |
| 517 | absent_mutator: Self::AbsentMutData<'a>, |
| 518 | ) -> Self::PresentMutData<'a> { |
Adrian Sadłocha | 8c08df5 | 2023-09-06 02:48:39 -0700 | [diff] [blame] | 519 | SettableValue::<VtableProxied>::set_on_absent( |
| 520 | absent_mutator.as_view().val(), |
| 521 | Private, |
| 522 | absent_mutator, |
| 523 | ) |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 524 | } |
| 525 | } |
| 526 | |
| 527 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 528 | struct VtableProxiedView { |
| 529 | val: i32, |
| 530 | } |
| 531 | |
| 532 | impl VtableProxiedView { |
| 533 | fn val(&self) -> i32 { |
| 534 | self.val |
| 535 | } |
| 536 | |
| 537 | fn read(msg: &MyMessage, vtable: &ProxyVtable) -> Self { |
| 538 | VtableProxiedView { val: (vtable.get)(msg) } |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | impl<'a> ViewProxy<'a> for VtableProxiedView { |
| 543 | type Proxied = VtableProxied; |
| 544 | |
| 545 | fn as_view(&self) -> View<'a, VtableProxied> { |
| 546 | *self |
| 547 | } |
| 548 | |
| 549 | fn into_view<'shorter>(self) -> View<'shorter, VtableProxied> |
| 550 | where |
| 551 | 'a: 'shorter, |
| 552 | { |
| 553 | self |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | #[derive(Debug)] |
| 558 | struct VtableProxiedMut<'a> { |
| 559 | msg: &'a mut MyMessage, |
| 560 | vtable: &'a ProxyVtable, |
| 561 | } |
| 562 | |
| 563 | impl<'a> ViewProxy<'a> for VtableProxiedMut<'a> { |
| 564 | type Proxied = VtableProxied; |
| 565 | |
| 566 | fn as_view(&self) -> View<'_, VtableProxied> { |
| 567 | VtableProxiedView::read(self.msg, self.vtable) |
| 568 | } |
| 569 | |
| 570 | fn into_view<'shorter>(self) -> View<'shorter, VtableProxied> |
| 571 | where |
| 572 | 'a: 'shorter, |
| 573 | { |
| 574 | VtableProxiedView::read(self.msg, self.vtable) |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | impl<'a> MutProxy<'a> for VtableProxiedMut<'a> { |
| 579 | fn as_mut(&mut self) -> Mut<'_, VtableProxied> { |
| 580 | VtableProxiedMut { msg: self.msg, vtable: self.vtable } |
| 581 | } |
| 582 | |
| 583 | fn into_mut<'shorter>(self) -> Mut<'shorter, VtableProxied> |
| 584 | where |
| 585 | 'a: 'shorter, |
| 586 | { |
| 587 | self |
| 588 | } |
| 589 | } |
| 590 | |
| 591 | impl SettableValue<VtableProxied> for View<'_, VtableProxied> { |
Jakob Buchgraber | ab11a0d | 2023-11-27 08:14:21 -0800 | [diff] [blame] | 592 | fn set_on<'a>(self, _private: Private, mutator: Mut<'a, VtableProxied>) |
| 593 | where |
| 594 | VtableProxied: 'a, |
| 595 | { |
Adrian Sadłocha | 8c08df5 | 2023-09-06 02:48:39 -0700 | [diff] [blame] | 596 | SettableValue::<VtableProxied>::set_on(self.val(), Private, mutator) |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 597 | } |
| 598 | |
| 599 | fn set_on_absent<'a>( |
| 600 | self, |
| 601 | _private: Private, |
| 602 | absent_mutator: <VtableProxied as ProxiedWithPresence>::AbsentMutData<'a>, |
| 603 | ) -> <VtableProxied as ProxiedWithPresence>::PresentMutData<'a> { |
Adrian Sadłocha | 8c08df5 | 2023-09-06 02:48:39 -0700 | [diff] [blame] | 604 | SettableValue::<VtableProxied>::set_on_absent(self.val(), Private, absent_mutator) |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 605 | } |
| 606 | } |
| 607 | |
| 608 | impl SettableValue<VtableProxied> for i32 { |
Jakob Buchgraber | ab11a0d | 2023-11-27 08:14:21 -0800 | [diff] [blame] | 609 | fn set_on<'a>(self, _private: Private, mutator: Mut<'a, VtableProxied>) |
| 610 | where |
| 611 | VtableProxied: 'a, |
| 612 | { |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 613 | (mutator.vtable.set)(mutator.msg, self) |
| 614 | } |
| 615 | |
| 616 | fn set_on_absent<'a>( |
| 617 | self, |
| 618 | _private: Private, |
| 619 | absent_mutator: <VtableProxied as ProxiedWithPresence>::AbsentMutData<'a>, |
| 620 | ) -> <VtableProxied as ProxiedWithPresence>::PresentMutData<'a> { |
| 621 | (absent_mutator.vtable.set)(absent_mutator.msg, self); |
| 622 | absent_mutator |
| 623 | } |
| 624 | } |
| 625 | |
| 626 | #[test] |
| 627 | fn test_field_entry() { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 628 | use googletest::prelude::*; |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 629 | let mut m1 = MyMessage::default(); |
| 630 | let mut m2 = MyMessage::default(); |
| 631 | |
| 632 | let mut m1_a = m1.a_mut(); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 633 | assert_that!(m1_a, matches_pattern!(Optional::Unset(_))); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 634 | |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 635 | assert_that!(m1_a.as_view().val(), eq(0)); |
| 636 | |
| 637 | assert_that!(m2.b().val(), eq(5)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 638 | |
| 639 | let mut m2_b = m2.b_mut(); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 640 | assert_that!(m2_b.is_unset(), eq(true)); |
| 641 | assert_that!(m2_b.as_view().val(), eq(5)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 642 | |
| 643 | m2_b.set(10); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 644 | assert_that!(m2_b.is_set(), eq(true)); |
| 645 | assert_that!(m2_b, matches_pattern!(Optional::Set(_))); |
| 646 | assert_that!(m2_b.as_view().val(), eq(10)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 647 | |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 648 | assert_that!(m1_a.or_default().as_view().val(), eq(0)); |
| 649 | assert_that!(m1.a_opt(), eq(Optional::Set(VtableProxiedView { val: 0 }))); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 650 | m1.a_mut().clear(); |
| 651 | |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 652 | assert_that!(m1.a().val(), eq(0)); |
| 653 | assert_that!(m1.b().val(), eq(5)); |
| 654 | assert_that!(m2.a().val(), eq(0)); |
| 655 | assert_that!(m2.b().val(), eq(10)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 656 | } |
| 657 | |
| 658 | #[test] |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 659 | fn test_or_set() { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 660 | use googletest::prelude::*; |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 661 | let mut m1 = MyMessage::default(); |
| 662 | let mut m2 = MyMessage::default(); |
| 663 | |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 664 | assert_that!(m1.a_mut().or_set(10).get().val(), eq(10)); |
| 665 | assert_that!(m1.a_opt(), eq(Optional::Set(VtableProxiedView { val: 10 }))); |
| 666 | assert_that!(m1.a_mut().or_set(20).get().val(), eq(10)); |
| 667 | assert_that!(m1.a_opt(), eq(Optional::Set(VtableProxiedView { val: 10 }))); |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 668 | |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 669 | assert_that!(m2.a_mut().or_set_with(|| m1.a().val() + m1.b().val()).get().val(), eq(15)); |
| 670 | assert_that!(m2.a_opt(), eq(Optional::Set(VtableProxiedView { val: 15 }))); |
| 671 | assert_that!(m2.a_mut().or_set_with(|| None::<i32>.unwrap()).get().val(), eq(15)); |
| 672 | assert_that!(m2.a_opt(), eq(Optional::Set(VtableProxiedView { val: 15 }))); |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 673 | } |
| 674 | |
| 675 | #[test] |
| 676 | fn test_into_optional_view() { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 677 | use googletest::prelude::*; |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 678 | let mut m1 = MyMessage::default(); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 679 | assert_that!( |
| 680 | m1.a_mut().into_optional_view(), |
| 681 | eq(Optional::Unset(VtableProxiedView { val: 0 })) |
| 682 | ); |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 683 | m1.a_mut().set(10); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 684 | assert_that!( |
| 685 | m1.a_mut().into_optional_view(), |
| 686 | eq(Optional::Set(VtableProxiedView { val: 10 })) |
| 687 | ); |
| 688 | assert_that!( |
| 689 | m1.b_mut().into_optional_view(), |
| 690 | eq(Optional::Unset(VtableProxiedView { val: 5 })) |
| 691 | ); |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 692 | } |
| 693 | |
| 694 | #[test] |
| 695 | fn test_try_into_mut() { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 696 | use googletest::prelude::*; |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 697 | let mut m1 = MyMessage::default(); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 698 | assert_that!(m1.a_mut().try_into_mut().is_none(), eq(true)); |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 699 | m1.a_mut().set(10); |
| 700 | let mut a_mut = m1.a_mut().try_into_mut().expect("field to be set"); |
| 701 | a_mut.set(20); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 702 | assert_that!(m1.a().val(), eq(20)); |
Protobuf Team Bot | d42b237 | 2023-07-10 18:24:33 -0700 | [diff] [blame] | 703 | } |
| 704 | |
| 705 | #[test] |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 706 | fn test_present_field() { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 707 | use googletest::prelude::*; |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 708 | let mut m = MyMessage::default(); |
| 709 | m.a_mut().set(10); |
| 710 | match m.a_mut() { |
| 711 | Optional::Set(mut present) => { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 712 | assert_that!(present.as_view().val(), eq(10)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 713 | present.set(20); |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 714 | assert_that!(present.as_view().val(), eq(20)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 715 | present.into_mut().set(30); |
| 716 | } |
| 717 | Optional::Unset(_) => unreachable!(), |
| 718 | } |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 719 | assert_that!(m.a_opt(), eq(Optional::Set(VtableProxiedView { val: 30 }))); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 720 | m.b_mut().set(20); |
| 721 | match m.b_mut() { |
| 722 | Optional::Set(present) => present.clear(), |
| 723 | Optional::Unset(_) => unreachable!(), |
| 724 | }; |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 725 | assert_that!(m.b_opt(), eq(Optional::Unset(VtableProxiedView { val: 5 }))); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 726 | } |
| 727 | |
| 728 | #[test] |
| 729 | fn test_absent_field() { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 730 | use googletest::prelude::*; |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 731 | let mut m = MyMessage::default(); |
| 732 | match m.a_mut() { |
| 733 | Optional::Set(_) => unreachable!(), |
| 734 | Optional::Unset(absent) => { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 735 | assert_that!(absent.as_view().val(), eq(0)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 736 | absent.set(20); |
| 737 | } |
| 738 | } |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 739 | assert_that!(m.a_opt(), eq(Optional::Set(VtableProxiedView { val: 20 }))); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 740 | match m.b_mut() { |
| 741 | Optional::Set(_) => unreachable!(), |
| 742 | Optional::Unset(absent) => { |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 743 | assert_that!(absent.as_view().val(), eq(5)); |
Protobuf Team Bot | 0d7a396 | 2023-07-10 11:51:22 -0700 | [diff] [blame] | 744 | absent.set_default(); |
| 745 | } |
| 746 | } |
Hong Shin | 1a5cdb9 | 2023-11-16 09:35:13 -0800 | [diff] [blame] | 747 | assert_that!(m.b_opt(), eq(Optional::Set(VtableProxiedView { val: 5 }))); |
Protobuf Team Bot | 263248e | 2023-06-22 09:14:12 -0700 | [diff] [blame] | 748 | } |
| 749 | } |