Skip to main content

buffa/
message_field.rs

1//! Optional message field wrapper that provides ergonomic access.
2//!
3//! `MessageField<T>` replaces `Option<Box<T>>` for optional/singular message
4//! fields. It dereferences to a default instance when unset, avoiding the
5//! `Option<Box<M>>` unwrapping ceremony that plagues prost-generated code.
6
7use alloc::boxed::Box;
8use core::fmt;
9use core::ops::{Deref, DerefMut};
10
11/// Provides access to a lazily-initialized, immutable default instance of a
12/// type.
13///
14/// Types that implement this trait can be used as the target of
15/// [`MessageField<T>`] dereferences when the field is unset. It is also a
16/// supertrait of [`Message`](crate::Message), so every generated message type
17/// must implement it.
18///
19/// **Codegen implements this automatically** for every generated message
20/// type. You only need to implement it by hand when manually implementing
21/// [`Message`](crate::Message) for a custom type.
22///
23/// # Recommended implementation
24///
25/// The pattern codegen uses — and the recommended pattern for manual
26/// implementations — stores the instance in a `static`
27/// [`once_cell::race::OnceBox`] (re-exported as
28/// `::buffa::__private::OnceBox`), which works in both `no_std + alloc` and
29/// `std` environments:
30///
31/// ```rust,ignore
32/// impl DefaultInstance for MyMessage {
33///     fn default_instance() -> &'static Self {
34///         static VALUE: ::buffa::__private::OnceBox<MyMessage>
35///             = ::buffa::__private::OnceBox::new();
36///         VALUE.get_or_init(|| ::alloc::boxed::Box::new(MyMessage::default()))
37///     }
38/// }
39/// ```
40///
41/// In `std`-only environments [`std::sync::OnceLock`] is also available and
42/// avoids the `alloc::boxed::Box` wrapping:
43///
44/// ```rust,ignore
45/// impl DefaultInstance for MyMessage {
46///     fn default_instance() -> &'static Self {
47///         static VALUE: std::sync::OnceLock<MyMessage> = std::sync::OnceLock::new();
48///         VALUE.get_or_init(MyMessage::default)
49///     }
50/// }
51/// ```
52pub trait DefaultInstance: Default + 'static {
53    /// Return a reference to the single default instance of this type.
54    fn default_instance() -> &'static Self;
55}
56
57/// Implement [`DefaultInstance`] for a message type via a lazily-initialized
58/// `OnceBox` singleton.
59///
60/// Emitted by generated code (one invocation per message struct) so the
61/// six-line singleton body lives here once instead of in every generated
62/// impl. Hand-written `Message` types may also use it.
63///
64/// ```rust,ignore
65/// buffa::impl_default_instance!(MyMessage);
66/// ```
67#[macro_export]
68macro_rules! impl_default_instance {
69    ($ty:ty) => {
70        impl $crate::DefaultInstance for $ty {
71            fn default_instance() -> &'static Self {
72                static VALUE: $crate::__private::OnceBox<$ty> = $crate::__private::OnceBox::new();
73                VALUE.get_or_init(|| {
74                    $crate::alloc::boxed::Box::new(<$ty as ::core::default::Default>::default())
75                })
76            }
77        }
78    };
79}
80
81/// The owned smart pointer backing a singular message field inside
82/// [`MessageField`].
83///
84/// The default is [`Box<T>`]; `buffa_build`'s `box_type_custom` knob substitutes
85/// any pointer that implements `ProtoBox<T>` — for example a `smallbox`-style
86/// pointer that stores small messages inline and avoids a heap allocation. The
87/// wire format is unchanged; only the in-memory ownership of the boxed message
88/// changes, and view types are unaffected.
89///
90/// There is intentionally no blanket impl — the built-in `Box<T>` impl below is
91/// the only one buffa provides. Because `ProtoBox` is buffa-owned, a *foreign*
92/// pointer cannot implement it directly (orphan rule); wrap it in a crate-local
93/// newtype, like the `ProtoString` newtype pattern.
94///
95/// # Why `DerefMut` (and why `Rc` / `Arc` are excluded)
96///
97/// The decoder merges a message field in place — it calls
98/// [`get_or_insert_default`](MessageField::get_or_insert_default) to obtain a
99/// `&mut T` and decodes into it. That requires `DerefMut`, which `Rc<T>` and
100/// `Arc<T>` cannot provide while shared. So this knob selects an **allocation
101/// strategy** (inline vs heap) for an exclusively-owned message, never a shared
102/// or copy-on-write pointer. A custom pointer that is not exclusively owned will
103/// fail to implement `ProtoBox` at its definition site, not silently misbehave.
104///
105/// # Thread-safety
106///
107/// `ProtoBox` deliberately does **not** require `Send`/`Sync` (unlike
108/// `ProtoString`/`ProtoBytes`/`ProtoList`). `MessageField<T>` is the universal
109/// message-field wrapper and has generic helpers; bounding the pointer
110/// `Send + Sync` would tighten `T: Send + Sync` onto all of them. It is also
111/// unnecessary: a generated message implements [`Message`](crate::Message),
112/// whose `Send + Sync` supertraits already require every field — and thus the
113/// pointer — to be `Send + Sync`, so a non-`Send` custom pointer is rejected at
114/// the message's `impl Message`, just less locally.
115///
116/// # Caveat
117///
118/// An inline pointer (e.g. `SmallBox`) inflates the *parent* struct by its
119/// inline-storage size for every such field, so it should be selected per field
120/// or prefix, never as a blanket default.
121///
122/// # Examples
123///
124/// A minimal crate-local newtype wrapping a foreign pointer (the `Clone` derive
125/// is only needed if the enclosing message derives `Clone`):
126///
127/// ```rust,ignore
128/// #[derive(Clone)]
129/// pub struct SmallBox<T>(pub smallbox::SmallBox<T, smallbox::space::S4>);
130///
131/// impl<T> core::ops::Deref for SmallBox<T> {
132///     type Target = T;
133///     fn deref(&self) -> &T { &self.0 }
134/// }
135/// impl<T> core::ops::DerefMut for SmallBox<T> {
136///     fn deref_mut(&mut self) -> &mut T { &mut self.0 }
137/// }
138/// impl<T> buffa::ProtoBox<T> for SmallBox<T> {
139///     fn new(value: T) -> Self { SmallBox(smallbox::smallbox!(value)) }
140///     fn into_inner(self) -> T { self.0.into_inner() }
141/// }
142/// ```
143///
144/// Then point a field at it: `box_type_custom("::my_crate::SmallBox<*>")`.
145#[rustversion::attr(
146    since(1.78),
147    diagnostic::on_unimplemented(
148        message = "`{Self}` cannot be used as a buffa custom box type",
149        note = "buffa owns `ProtoBox`, so a foreign type can't implement it directly (orphan rule). \
150                Wrap it in a crate-local newtype and implement `ProtoBox` on the newtype. \
151                See the `custom-types` example in the buffa repository for a template."
152    )
153)]
154pub trait ProtoBox<T>: Deref<Target = T> + DerefMut {
155    /// Box a freshly-decoded or constructed message value.
156    fn new(value: T) -> Self;
157
158    /// Unwrap the pointer, returning the owned message value (the
159    /// [`take`](MessageField::take) / [`into_option`](MessageField::into_option)
160    /// path).
161    fn into_inner(self) -> T;
162}
163
164impl<T> ProtoBox<T> for Box<T> {
165    #[inline]
166    fn new(value: T) -> Self {
167        Box::new(value)
168    }
169
170    #[inline]
171    fn into_inner(self) -> T {
172        *self
173    }
174}
175
176// The default pointer must always satisfy the bound; freeze that invariant
177// against future changes to the trait's supertraits.
178const _: fn() = || {
179    fn assert_proto_box<P: ProtoBox<T>, T>() {}
180    assert_proto_box::<Box<u32>, u32>();
181};
182
183/// A wrapper for optional message fields that provides transparent access
184/// to a default instance when the field is not set.
185///
186/// This type is used for singular message fields in generated code. It avoids
187/// the ergonomic pain of `Option<Box<M>>` while still being heap-allocated
188/// only when set (no allocation for unset fields).
189///
190/// The pointer type `P` is pluggable (default [`Box<T>`]); see [`ProtoBox`].
191/// Because `P` has a default, a *standalone* construction with no pinning
192/// context needs a type annotation — write `let f: MessageField<Foo> =
193/// MessageField::some(x);` (or `MessageField::<Foo>::some(x)`). In the common
194/// cases — a struct-literal field (`Outer { inner: MessageField::some(x), .. }`)
195/// or an assignment to a typed field — `P` is inferred from the target and no
196/// annotation is needed.
197///
198/// # Access patterns
199///
200/// ```rust,ignore
201/// // Reading through an unset field gives the default:
202/// let msg = Outer::default();
203/// assert_eq!(msg.inner.name, "");  // No unwrap needed, derefs to default
204///
205/// // Check if set:
206/// if msg.inner.is_set() { ... }
207///
208/// // Set a value:
209/// msg.inner = MessageField::some(Inner { name: "hello".into(), ..Default::default() });
210///
211/// // Clear:
212/// msg.inner = MessageField::none();
213/// ```
214pub struct MessageField<T: Default, P = Box<T>> {
215    inner: Option<P>,
216    _marker: core::marker::PhantomData<T>,
217}
218
219impl<T: Default, P: ProtoBox<T>> MessageField<T, P> {
220    /// Create a `MessageField` with no value set.
221    #[inline]
222    pub const fn none() -> Self {
223        Self {
224            inner: None,
225            _marker: core::marker::PhantomData,
226        }
227    }
228
229    /// Create a `MessageField` with a value.
230    #[inline]
231    pub fn some(value: T) -> Self {
232        Self {
233            inner: Some(<P as ProtoBox<T>>::new(value)),
234            _marker: core::marker::PhantomData,
235        }
236    }
237
238    /// Create a `MessageField` from an already-constructed pointer, without
239    /// unwrapping and re-boxing the value.
240    ///
241    /// Prefer this over `MessageField::some(p.into_inner())` when you already
242    /// hold a `P` — for an inline pointer (e.g. `SmallBox`) the latter would
243    /// move the value out and re-store it, defeating the point. The generic
244    /// counterpart to [`from_box`](Self::from_box) (which is `Box`-only).
245    #[inline]
246    pub fn from_pointer(value: P) -> Self {
247        Self {
248            inner: Some(value),
249            _marker: core::marker::PhantomData,
250        }
251    }
252
253    /// Returns `true` if the field has a value set.
254    #[inline]
255    pub fn is_set(&self) -> bool {
256        self.inner.is_some()
257    }
258
259    /// Returns `true` if the field has no value set.
260    #[inline]
261    pub fn is_unset(&self) -> bool {
262        self.inner.is_none()
263    }
264
265    /// Get a reference to the inner value, or `None` if unset.
266    #[inline]
267    pub fn as_option(&self) -> Option<&T> {
268        self.inner.as_deref()
269    }
270
271    /// Get a mutable reference to the inner value, or `None` if unset.
272    #[inline]
273    pub fn as_option_mut(&mut self) -> Option<&mut T> {
274        self.inner.as_deref_mut()
275    }
276
277    /// Take the inner value, leaving the field unset.
278    #[inline]
279    pub fn take(&mut self) -> Option<T> {
280        self.inner.take().map(<P as ProtoBox<T>>::into_inner)
281    }
282
283    /// Get a mutable reference to the value, initializing to the default if unset.
284    #[inline]
285    pub fn get_or_insert_default(&mut self) -> &mut T {
286        // `&mut P` coerces to `&mut T` via `P: DerefMut<Target = T>`. Uses
287        // `ProtoBox::new` rather than `Option::get_or_insert_default` so no
288        // `P: Default` bound is needed (a custom pointer need not be `Default`).
289        self.inner
290            .get_or_insert_with(|| <P as ProtoBox<T>>::new(T::default()))
291    }
292
293    /// Call `f` with a mutable reference to the inner value, initializing to
294    /// the default if the field is currently unset.
295    ///
296    /// This is the ergonomic write counterpart to the transparent read
297    /// provided by `Deref`. Instead of calling `get_or_insert_default` once
298    /// per assignment:
299    ///
300    /// ```rust,ignore
301    /// msg.address.get_or_insert_default().street = "123 Main St".into();
302    /// msg.address.get_or_insert_default().city   = "Springfield".into();
303    /// ```
304    ///
305    /// use `modify` to initialize the field once and set all sub-fields in
306    /// the closure:
307    ///
308    /// ```rust,ignore
309    /// msg.address.modify(|a| {
310    ///     a.street = "123 Main St".into();
311    ///     a.city   = "Springfield".into();
312    /// });
313    /// ```
314    #[inline]
315    pub fn modify<F: FnOnce(&mut T)>(&mut self, f: F) {
316        f(self.get_or_insert_default());
317    }
318
319    /// Consume the field, returning `Some(T)` if set or `None` if unset.
320    ///
321    /// This unboxes the inner value. For in-place extraction that leaves the
322    /// field unset without consuming the enclosing struct, see [`take`](Self::take).
323    #[inline]
324    pub fn into_option(self) -> Option<T> {
325        self.inner.map(<P as ProtoBox<T>>::into_inner)
326    }
327
328    /// Consume the field, returning the inner value.
329    ///
330    /// Equivalent in effect to `into_option().unwrap()`, with a clearer
331    /// panic message. Prefer [`ok_or`](Self::ok_or) /
332    /// [`ok_or_else`](Self::ok_or_else) when an unset field should produce
333    /// an error rather than a panic.
334    ///
335    /// # Panics
336    ///
337    /// Panics if the field is unset.
338    #[inline]
339    #[track_caller]
340    pub fn unwrap(self) -> T {
341        match self.inner {
342            Some(b) => <P as ProtoBox<T>>::into_inner(b),
343            None => panic!("called `MessageField::unwrap()` on an unset field"),
344        }
345    }
346
347    /// Consume the field, returning the inner value, with a custom panic
348    /// message if unset.
349    ///
350    /// Mirrors [`Option::expect`]. Prefer [`ok_or`](Self::ok_or) /
351    /// [`ok_or_else`](Self::ok_or_else) when an unset field should produce
352    /// an error rather than a panic.
353    ///
354    /// # Panics
355    ///
356    /// Panics with `msg` if the field is unset.
357    #[inline]
358    #[track_caller]
359    pub fn expect(self, msg: &str) -> T {
360        match self.inner {
361            Some(b) => <P as ProtoBox<T>>::into_inner(b),
362            None => panic!("{msg}"),
363        }
364    }
365
366    /// Consume the field, returning `Ok(T)` if set or `Err(err)` if unset.
367    ///
368    /// Mirrors [`Option::ok_or`]. Useful for enforcing presence of
369    /// semantically-required fields that the proto schema leaves optional:
370    ///
371    /// ```rust,ignore
372    /// let cmd = request.normalized_command.ok_or(Error::MissingCommand)?;
373    /// ```
374    #[inline]
375    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
376        match self.inner {
377            Some(b) => Ok(<P as ProtoBox<T>>::into_inner(b)),
378            None => Err(err),
379        }
380    }
381
382    /// Consume the field, returning `Ok(T)` if set or `Err(err())` if unset.
383    ///
384    /// Mirrors [`Option::ok_or_else`]. The closure is only called if the
385    /// field is unset, so use this over [`ok_or`](Self::ok_or) when
386    /// constructing the error is non-trivial:
387    ///
388    /// ```rust,ignore
389    /// let cmd = request.normalized_command.ok_or_else(|| {
390    ///     ConnectError::invalid_argument("missing normalized_command in request")
391    /// })?;
392    /// ```
393    #[inline]
394    pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
395        match self.inner {
396            Some(b) => Ok(<P as ProtoBox<T>>::into_inner(b)),
397            None => Err(err()),
398        }
399    }
400}
401
402impl<T: Default> MessageField<T, Box<T>> {
403    /// Create a `MessageField` from a boxed value.
404    ///
405    /// Specific to the default `Box<T>` pointer; for a custom [`ProtoBox`]
406    /// pointer construct with [`some`](Self::some).
407    #[inline]
408    pub fn from_box(value: Box<T>) -> Self {
409        Self {
410            inner: Some(value),
411            _marker: core::marker::PhantomData,
412        }
413    }
414}
415
416impl<T: Default, P: ProtoBox<T>> Default for MessageField<T, P> {
417    #[inline]
418    fn default() -> Self {
419        Self::none()
420    }
421}
422
423impl<T: DefaultInstance, P: ProtoBox<T>> Deref for MessageField<T, P> {
424    type Target = T;
425
426    #[inline]
427    fn deref(&self) -> &T {
428        match &self.inner {
429            // `&P` coerces to `&T` via `P: Deref<Target = T>`.
430            Some(value) => value,
431            None => T::default_instance(),
432        }
433    }
434}
435
436impl<T: Default + Clone, P: ProtoBox<T> + Clone> Clone for MessageField<T, P> {
437    fn clone(&self) -> Self {
438        Self {
439            inner: self.inner.clone(),
440            _marker: core::marker::PhantomData,
441        }
442    }
443}
444
445impl<T: DefaultInstance + PartialEq, P: ProtoBox<T>> PartialEq for MessageField<T, P> {
446    fn eq(&self, other: &Self) -> bool {
447        // Compare the pointed-to `T` values (via `**`), not the pointers, so no
448        // `P: PartialEq` bound is needed and a set-to-default field equals an
449        // unset one.
450        match (&self.inner, &other.inner) {
451            (Some(a), Some(b)) => **a == **b,
452            (None, None) => true,
453            // An unset field equals a set-to-default field. Use default_instance()
454            // to avoid allocating a temporary value for the comparison.
455            (Some(a), None) => **a == *T::default_instance(),
456            (None, Some(b)) => *T::default_instance() == **b,
457        }
458    }
459}
460
461impl<T: DefaultInstance + Eq + PartialEq, P: ProtoBox<T>> Eq for MessageField<T, P> {}
462
463impl<T: Default + fmt::Debug, P: ProtoBox<T>> fmt::Debug for MessageField<T, P> {
464    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465        match &self.inner {
466            // Format the pointed-to `T` (via `&**`), so no `P: Debug` bound.
467            Some(value) => f.debug_tuple("MessageField::Set").field(&**value).finish(),
468            None => f.write_str("MessageField::Unset"),
469        }
470    }
471}
472
473impl<T: Default, P: ProtoBox<T>> From<Option<T>> for MessageField<T, P> {
474    fn from(opt: Option<T>) -> Self {
475        match opt {
476            Some(v) => Self::some(v),
477            None => Self::none(),
478        }
479    }
480}
481
482impl<T: Default, P: ProtoBox<T>> From<MessageField<T, P>> for Option<T> {
483    /// Unbox a `MessageField` into an `Option<T>`; equivalent to
484    /// [`MessageField::into_option`].
485    #[inline]
486    fn from(field: MessageField<T, P>) -> Self {
487        field.into_option()
488    }
489}
490
491impl<T: Default, P: ProtoBox<T>> From<T> for MessageField<T, P> {
492    fn from(value: T) -> Self {
493        Self::some(value)
494    }
495}
496
497#[cfg(feature = "json")]
498impl<T: Default + serde::Serialize, P: ProtoBox<T>> serde::Serialize for MessageField<T, P> {
499    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
500        match self.inner.as_deref() {
501            Some(v) => s.serialize_some(v),
502            None => s.serialize_none(),
503        }
504    }
505}
506
507#[cfg(feature = "json")]
508impl<'de, T: Default + serde::Deserialize<'de>, P: ProtoBox<T>> serde::Deserialize<'de>
509    for MessageField<T, P>
510{
511    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
512        Option::<T>::deserialize(d).map(|opt| match opt {
513            Some(v) => Self::some(v),
514            None => Self::none(),
515        })
516    }
517}
518
519#[cfg(feature = "arbitrary")]
520impl<'a, T: Default + arbitrary::Arbitrary<'a>, P: ProtoBox<T>> arbitrary::Arbitrary<'a>
521    for MessageField<T, P>
522{
523    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
524        Ok(if bool::arbitrary(u)? {
525            MessageField::some(T::arbitrary(u)?)
526        } else {
527            MessageField::none()
528        })
529    }
530}
531
532#[cfg(test)]
533mod tests {
534    use super::*;
535
536    #[derive(Clone, Debug, Default, PartialEq)]
537    struct Inner {
538        value: i32,
539        name: alloc::string::String,
540    }
541
542    // Via the exported macro, which doubles as its unit test (hygiene and
543    // `$crate` path resolution).
544    crate::impl_default_instance!(Inner);
545
546    #[test]
547    fn impl_default_instance_macro_returns_singleton() {
548        let a: &'static Inner = Inner::default_instance();
549        let b: &'static Inner = Inner::default_instance();
550        assert!(core::ptr::eq(a, b), "singleton must be a single allocation");
551        assert_eq!(a, &Inner::default());
552    }
553
554    #[test]
555    fn test_unset_derefs_to_default() {
556        let field: MessageField<Inner> = MessageField::none();
557        assert!(!field.is_set());
558        assert_eq!(field.value, 0);
559        assert_eq!(field.name, "");
560    }
561
562    #[test]
563    fn test_set_derefs_to_value() {
564        let field: MessageField<Inner> = MessageField::some(Inner {
565            value: 42,
566            name: "hello".into(),
567        });
568        assert!(field.is_set());
569        assert_eq!(field.value, 42);
570        assert_eq!(field.name, "hello");
571    }
572
573    #[test]
574    fn test_get_or_insert_default() {
575        let mut field: MessageField<Inner> = MessageField::none();
576        assert!(!field.is_set());
577        field.get_or_insert_default().value = 10;
578        assert!(field.is_set());
579        assert_eq!(field.value, 10);
580    }
581
582    #[test]
583    fn test_equality() {
584        let a: MessageField<Inner> = MessageField::none();
585        let b: MessageField<Inner> = MessageField::some(Inner::default());
586        // An unset field and a set-to-default field are equal.
587        assert_eq!(a, b);
588    }
589
590    #[test]
591    fn test_unwrap_set() {
592        let field: MessageField<Inner> = MessageField::some(Inner {
593            value: 7,
594            name: "x".into(),
595        });
596        let inner = field.unwrap();
597        assert_eq!(inner.value, 7);
598    }
599
600    #[test]
601    #[should_panic(expected = "called `MessageField::unwrap()` on an unset field")]
602    fn test_unwrap_unset_panics() {
603        let field: MessageField<Inner> = MessageField::none();
604        let _ = field.unwrap();
605    }
606
607    #[test]
608    #[should_panic(expected = "address is required")]
609    fn test_expect_unset_panics_with_message() {
610        let field: MessageField<Inner> = MessageField::none();
611        let _ = field.expect("address is required");
612    }
613
614    #[test]
615    fn test_from_message_field_for_option_roundtrips() {
616        let inner = Inner {
617            value: 3,
618            name: "rt".into(),
619        };
620        let field: MessageField<Inner> = Some(inner.clone()).into();
621        let back: Option<Inner> = field.into();
622        assert_eq!(back, Some(inner));
623
624        let none_field: MessageField<Inner> = MessageField::none();
625        let none_back: Option<Inner> = none_field.into();
626        assert_eq!(none_back, None);
627    }
628
629    #[test]
630    fn test_take() {
631        let mut field: MessageField<Inner> = MessageField::some(Inner {
632            value: 7,
633            name: "taken".into(),
634        });
635        let taken = field.take();
636        assert!(field.is_unset());
637        assert_eq!(taken.unwrap().value, 7);
638    }
639
640    #[test]
641    fn test_clone() {
642        let field: MessageField<Inner> = MessageField::some(Inner {
643            value: 99,
644            name: "clone".into(),
645        });
646        let cloned = field.clone();
647        assert_eq!(field, cloned);
648    }
649
650    #[test]
651    fn test_modify_initializes_unset_field() {
652        let mut field: MessageField<Inner> = MessageField::none();
653        field.modify(|inner| {
654            inner.value = 42;
655            inner.name = "hello".into();
656        });
657        assert!(field.is_set());
658        assert_eq!(field.value, 42);
659        assert_eq!(field.name, "hello");
660    }
661
662    #[test]
663    fn test_modify_updates_already_set_field() {
664        let mut field: MessageField<Inner> = MessageField::some(Inner {
665            value: 1,
666            name: "original".into(),
667        });
668        field.modify(|inner| {
669            inner.value = 99;
670        });
671        // Existing value is mutated in place, not reset to default.
672        assert_eq!(field.value, 99);
673        assert_eq!(field.name, "original");
674    }
675
676    #[test]
677    fn test_modify_multiple_fields_in_one_call() {
678        // Demonstrates the primary motivation: one initialization, many assignments.
679        let mut field: MessageField<Inner> = MessageField::none();
680        field.modify(|inner| {
681            inner.value = 10;
682            inner.name = "multi".into();
683        });
684        assert_eq!(field.value, 10);
685        assert_eq!(field.name, "multi");
686    }
687
688    #[test]
689    fn test_modify_noop_still_initializes_unset_field() {
690        // Even a no-op closure causes the field to become set (to the default).
691        let mut field: MessageField<Inner> = MessageField::none();
692        field.modify(|_| {});
693        assert!(field.is_set());
694        assert_eq!(field.value, 0);
695        assert_eq!(field.name, "");
696    }
697
698    #[test]
699    fn test_modify_closure_can_move_captured_values() {
700        // Verifies that the FnOnce bound is in effect: a closure that moves a
701        // captured value (String) into the field compiles and works correctly.
702        let mut field: MessageField<Inner> = MessageField::none();
703        let name = alloc::string::String::from("moved");
704        field.modify(|inner| {
705            inner.name = name; // moves `name` -- only valid with FnOnce
706        });
707        assert_eq!(field.name, "moved");
708    }
709
710    #[cfg(feature = "json")]
711    mod serde_tests {
712        use super::*;
713
714        #[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
715        struct Msg {
716            value: i32,
717        }
718
719        #[test]
720        fn unset_serializes_as_null() {
721            let f: MessageField<Msg> = MessageField::none();
722            assert_eq!(serde_json::to_string(&f).unwrap(), "null");
723        }
724
725        #[test]
726        fn set_serializes_as_inner_json() {
727            let f: MessageField<Msg> = MessageField::some(Msg { value: 42 });
728            let json = serde_json::to_string(&f).unwrap();
729            assert_eq!(json, r#"{"value":42}"#);
730        }
731
732        #[test]
733        fn null_deserializes_as_unset() {
734            let f: MessageField<Msg> = serde_json::from_str("null").unwrap();
735            assert!(f.is_unset());
736        }
737
738        #[test]
739        fn object_deserializes_as_set() {
740            let f: MessageField<Msg> = serde_json::from_str(r#"{"value":7}"#).unwrap();
741            assert!(f.is_set());
742            assert_eq!(f.as_option().unwrap().value, 7);
743        }
744
745        #[test]
746        fn round_trip_set_field() {
747            let original: MessageField<Msg> = MessageField::some(Msg { value: 99 });
748            let json = serde_json::to_string(&original).unwrap();
749            let recovered: MessageField<Msg> = serde_json::from_str(&json).unwrap();
750            assert_eq!(
751                original.as_option().unwrap().value,
752                recovered.as_option().unwrap().value
753            );
754        }
755    }
756
757    #[test]
758    fn test_into_option_unboxes() {
759        let field: MessageField<Inner> = MessageField::some(Inner {
760            value: 5,
761            name: "x".into(),
762        });
763        // Type annotation asserts this is Option<Inner>, not Option<Box<Inner>>.
764        let opt: Option<Inner> = field.into_option();
765        assert_eq!(opt.unwrap().value, 5);
766
767        let field: MessageField<Inner> = MessageField::none();
768        assert!(field.into_option().is_none());
769    }
770
771    #[test]
772    fn test_ok_or() {
773        let set: MessageField<Inner> = MessageField::some(Inner {
774            value: 1,
775            name: "set".into(),
776        });
777        let r: Result<Inner, &str> = set.ok_or("missing");
778        assert_eq!(r.unwrap().value, 1);
779
780        let unset: MessageField<Inner> = MessageField::none();
781        assert_eq!(unset.ok_or("missing"), Err("missing"));
782    }
783
784    #[test]
785    fn test_ok_or_else() {
786        let set: MessageField<Inner> = MessageField::some(Inner {
787            value: 2,
788            name: "set".into(),
789        });
790        assert_eq!(set.ok_or_else(|| "unreachable").unwrap().value, 2);
791
792        let unset: MessageField<Inner> = MessageField::none();
793        assert_eq!(unset.ok_or_else(|| "missing"), Err("missing"));
794    }
795
796    #[test]
797    fn test_ok_or_else_closure_not_called_when_set() {
798        let set: MessageField<Inner> = MessageField::some(Inner::default());
799        let _ = set.ok_or_else(|| -> &str { panic!("closure must not run") });
800    }
801
802    #[test]
803    fn test_ok_or_else_partial_move() {
804        // The motivating pattern: destructure several required fields out of
805        // an owned request struct, one ok_or_else per field. Each is a partial
806        // move; the remaining fields stay accessible.
807        #[derive(Default)]
808        struct Request {
809            a: MessageField<Inner>,
810            b: MessageField<Inner>,
811        }
812        let req = Request {
813            a: MessageField::some(Inner {
814                value: 1,
815                ..Default::default()
816            }),
817            b: MessageField::some(Inner {
818                value: 2,
819                ..Default::default()
820            }),
821        };
822        let a = req.a.ok_or_else(|| "missing a").unwrap();
823        let b = req.b.ok_or_else(|| "missing b").unwrap();
824        assert_eq!(a.value, 1);
825        assert_eq!(b.value, 2);
826    }
827
828    #[test]
829    fn test_from_conversions() {
830        let field: MessageField<Inner> = Inner {
831            value: 1,
832            name: "from".into(),
833        }
834        .into();
835        assert!(field.is_set());
836
837        let field: MessageField<Inner> = None.into();
838        assert!(field.is_unset());
839
840        let field: MessageField<Inner> = Some(Inner {
841            value: 2,
842            name: "some".into(),
843        })
844        .into();
845        assert!(field.is_set());
846    }
847}