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}