diff --git a/rust/optional.rs b/rust/optional.rs index 1241f8a6af..c2d2b5dad7 100644 --- a/rust/optional.rs +++ b/rust/optional.rs @@ -122,9 +122,34 @@ impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> FieldEntry<'msg, T> { } } + /// Gets a mutator for this field. Sets to the given `val` if not set. + /// + /// If the field is already set, `val` is ignored. + pub fn or_set(self, val: impl SettableValue) -> Mut<'msg, T> { + self.or_set_with(move || val) + } + + /// Gets a mutator for this field. Sets using the given `val` function if + /// not set. + /// + /// If the field is already set, `val` is not invoked. + pub fn or_set_with(self, val: impl FnOnce() -> S) -> Mut<'msg, T> + where + S: SettableValue, + { + match self { + Optional::Set(x) => x.into_mut(), + Optional::Unset(x) => x.set(val()).into_mut(), + } + } + /// Sets the value of this field to `val`. /// /// Equivalent to `self.or_default().set(val)`, but does not consume `self`. + /// + /// `set` has the same parameters as in [`MutProxy`], so making a field + /// `optional` will switch to using this method. This makes transitioning + /// from implicit to explicit presence easier. pub fn set(&mut self, val: impl SettableValue) { transform_mut(self, |mut self_| match self_ { Optional::Set(ref mut present) => { @@ -142,6 +167,41 @@ impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> FieldEntry<'msg, T> { absent => absent, }) } + + /// Gets an immutable view of this field, using its default value if not + /// set. This is shorthand for `as_view`. + /// + /// This provides a shorter lifetime than `into_view` but can also be called + /// multiple times - if the result of `get` is not living long enough + /// for your use, use that instead. + /// + /// `get` has the same parameters as in [`MutProxy`], so making a field + /// `optional` will switch to using this method. This makes transitioning + /// from implicit to explicit presence easier. + pub fn get(&self) -> View<'_, T> { + self.as_view() + } + + /// Converts to an immutable view of this optional field, preserving the + /// field's presence. + pub fn into_optional_view(self) -> Optional> { + let is_set = self.is_set(); + Optional::new(self.into_view(), is_set) + } + + /// Returns a field mutator if the field is set. + /// + /// Returns `None` if the field is not set. This does not affect `is_set()`. + /// + /// This returns `Option` and _not_ `Optional` since returning a defaulted + /// `Mut` would require mutating the presence of the field - for that + /// behavior, use `or_default()`. + pub fn try_into_mut(self) -> Option> { + match self { + Optional::Set(x) => Some(x.into_mut()), + Optional::Unset(_) => None, + } + } } impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> ViewProxy<'msg> for FieldEntry<'msg, T> { @@ -189,6 +249,16 @@ impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> PresentField<'msg, T> { Self { inner } } + /// Gets an immutable view of this present field. This is shorthand for + /// `as_view`. + /// + /// This provides a shorter lifetime than `into_view` but can also be called + /// multiple times - if the result of `get` is not living long enough + /// for your use, use that instead. + pub fn get(&self) -> View<'_, T> { + self.as_view() + } + pub fn set(&mut self, val: impl SettableValue) { val.set_on(Private, self.as_mut()) } @@ -259,9 +329,10 @@ impl<'msg, T: ProxiedWithPresence + ?Sized> AbsentField<'msg, T> { /// Gets the default value for this unset field. /// - /// This is the same value that the primitive accessor would provide. - pub fn default_value(self) -> View<'msg, T> { - self.into_view() + /// This is the same value that the primitive accessor would provide, though + /// with the shorter lifetime of `as_view`. + pub fn default_value(&self) -> View<'_, T> { + self.as_view() } /// See [`FieldEntry::set`]. Note that this consumes and returns a @@ -609,6 +680,41 @@ mod tests { assert_eq!(m2.b().val(), 10); } + #[test] + fn test_or_set() { + let mut m1 = MyMessage::default(); + let mut m2 = MyMessage::default(); + + assert_eq!(m1.a_mut().or_set(10).get().val(), 10); + assert_eq!(m1.a_opt(), Optional::Set(VtableProxiedView { val: 10 })); + assert_eq!(m1.a_mut().or_set(20).get().val(), 10); + assert_eq!(m1.a_opt(), Optional::Set(VtableProxiedView { val: 10 })); + + assert_eq!(m2.a_mut().or_set_with(|| m1.a().val() + m1.b().val()).get().val(), 15); + assert_eq!(m2.a_opt(), Optional::Set(VtableProxiedView { val: 15 })); + assert_eq!(m2.a_mut().or_set_with(|| None::.unwrap()).get().val(), 15); + assert_eq!(m2.a_opt(), Optional::Set(VtableProxiedView { val: 15 })); + } + + #[test] + fn test_into_optional_view() { + let mut m1 = MyMessage::default(); + assert_eq!(m1.a_mut().into_optional_view(), Optional::Unset(VtableProxiedView { val: 0 })); + m1.a_mut().set(10); + assert_eq!(m1.a_mut().into_optional_view(), Optional::Set(VtableProxiedView { val: 10 })); + assert_eq!(m1.b_mut().into_optional_view(), Optional::Unset(VtableProxiedView { val: 5 })); + } + + #[test] + fn test_try_into_mut() { + let mut m1 = MyMessage::default(); + assert!(m1.a_mut().try_into_mut().is_none()); + m1.a_mut().set(10); + let mut a_mut = m1.a_mut().try_into_mut().expect("field to be set"); + a_mut.set(20); + assert_eq!(m1.a().val(), 20); + } + #[test] fn test_present_field() { let mut m = MyMessage::default(); diff --git a/rust/proxied.rs b/rust/proxied.rs index b879418bad..e5c4bb0f5d 100644 --- a/rust/proxied.rs +++ b/rust/proxied.rs @@ -170,6 +170,15 @@ pub trait ViewProxy<'a>: 'a + Sized + Sync + Unpin + Sized + Debug { /// This trait is intentionally made non-object-safe to prevent a potential /// future incompatible change. pub trait MutProxy<'a>: ViewProxy<'a> { + /// Gets an immutable view of this field. This is shorthand for `as_view`. + /// + /// This provides a shorter lifetime than `into_view` but can also be called + /// multiple times - if the result of `get` is not living long enough + /// for your use, use that instead. + fn get(&self) -> View<'_, Self::Proxied> { + self.as_view() + } + /// Sets this field to the given `val`. /// /// Any borrowed data from `val` will be cloned.