qubit_value/value_container.rs
1// =============================================================================
2// Copyright (c) 2025 - 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9//! Explicit scalar-or-collection value storage.
10
11#[cfg(feature = "converter")]
12use qubit_datatype::ConversionLimits;
13#[cfg(feature = "converter")]
14use qubit_datatype::ConversionPolicy;
15#[cfg(feature = "converter")]
16use qubit_datatype::ConversionSession;
17#[cfg(feature = "converter")]
18use qubit_datatype::DataConversionTarget;
19use qubit_datatype::DataType;
20#[cfg(feature = "converter")]
21use qubit_datatype::ScalarStringDataConverters;
22
23use crate::MultiValues;
24use crate::StrictValueRead;
25use crate::Value;
26use crate::ValueError;
27use crate::ValueResult;
28use crate::multi_values::MultiValuesRepr;
29#[cfg(feature = "converter")]
30use crate::value::ValueRef;
31use crate::value::ValueRepr;
32
33/// A typed value whose scalar or collection shape is explicit.
34///
35/// The shape is never inferred from collection length. In particular,
36/// `Scalar(Value::Int32(42))` and
37/// `Collection(MultiValues::Int32(vec![42]))` remain distinguishable through
38/// conversion and serialization boundaries.
39///
40/// # Examples
41///
42/// ```
43/// use qubit_value::ValueContainer;
44///
45/// let scalar = ValueContainer::from(42_i32);
46/// let collection = ValueContainer::from(vec![42_i32]);
47/// assert!(scalar.is_scalar());
48/// assert!(collection.is_collection());
49/// ```
50#[must_use]
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52pub enum ValueContainer {
53 /// One typed value.
54 Scalar(
55 /// Stored scalar value.
56 Value,
57 ),
58 /// A homogeneous typed collection.
59 Collection(
60 /// Stored homogeneous collection.
61 MultiValues,
62 ),
63}
64
65/// Implements scalar and collection conversions from the shared value table.
66macro_rules! impl_value_container_from_table {
67 (
68 ;
69 $(
70 (
71 [$($cfg:meta),*],
72 $variant:ident,
73 $type:ty,
74 $data_type:expr,
75 $materialization:ident,
76 $json_class:ident,
77 $number_projection:ident,
78 $value_doc:literal,
79 $multi_doc:literal
80 $(, $_wire:tt)*
81 )
82 ),+ $(,)?
83 ) => {
84 $(
85 $(#[$cfg])*
86 impl From<$type> for ValueContainer {
87 #[inline(always)]
88 fn from(value: $type) -> Self {
89 Self::Scalar(Value::$variant(value))
90 }
91 }
92
93 $(#[$cfg])*
94 impl From<Vec<$type>> for ValueContainer {
95 #[inline(always)]
96 fn from(values: Vec<$type>) -> Self {
97 Self::Collection(MultiValues::$variant(values))
98 }
99 }
100
101 $(#[$cfg])*
102 impl From<&[$type]> for ValueContainer {
103 #[inline]
104 fn from(values: &[$type]) -> Self {
105 Self::Collection(MultiValues::$variant(values.to_vec()))
106 }
107 }
108
109 $(#[$cfg])*
110 impl From<&Vec<$type>> for ValueContainer {
111 #[inline]
112 fn from(values: &Vec<$type>) -> Self {
113 Self::Collection(MultiValues::$variant(values.clone()))
114 }
115 }
116
117 $(#[$cfg])*
118 impl<const N: usize> From<[$type; N]> for ValueContainer {
119 #[inline]
120 fn from(values: [$type; N]) -> Self {
121 Self::Collection(MultiValues::$variant(Vec::from(values)))
122 }
123 }
124
125 $(#[$cfg])*
126 impl<const N: usize> From<&[$type; N]> for ValueContainer {
127 #[inline]
128 fn from(values: &[$type; N]) -> Self {
129 Self::Collection(MultiValues::$variant(values.to_vec()))
130 }
131 }
132 )+
133 };
134}
135
136for_each_value_type!(impl_value_container_from_table);
137
138/// Builds a typed collection from one or two same-typed scalar values.
139macro_rules! value_container_pair_match {
140 ($first:expr, $second:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal $(, $_wire:tt)*)),+ $(,)?) => {
141 match ($first.repr, $second.repr) {
142 $(
143 $(#[$cfg])*
144 (ValueRepr::$variant(first), ValueRepr::$variant(second)) => {
145 MultiValues::$variant(vec![
146 value_storage_into_multi!($variant, first),
147 value_storage_into_multi!($variant, second),
148 ])
149 }
150 )+
151 $(
152 $(#[$cfg])*
153 (ValueRepr::Unset(_), ValueRepr::$variant(second)) => {
154 MultiValues::$variant(vec![value_storage_into_multi!($variant, second)])
155 }
156 )+
157 _ => unreachable!(),
158 }
159 };
160}
161
162/// Pushes a same-typed scalar directly into collection storage.
163macro_rules! value_container_push_match {
164 ($collection:expr, $value:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal $(, $_wire:tt)*)),+ $(,)?) => {
165 match (&mut $collection.repr, $value.repr) {
166 $(
167 $(#[$cfg])*
168 (MultiValuesRepr::$variant(values), ValueRepr::$variant(value)) => {
169 values.push(value_storage_into_multi!($variant, value))
170 },
171 )+
172 $(
173 $(#[$cfg])*
174 (slot @ MultiValuesRepr::Unset(_), ValueRepr::$variant(value)) => {
175 *slot = MultiValuesRepr::$variant(vec![value_storage_into_multi!($variant, value)]);
176 }
177 )+
178 _ => unreachable!(),
179 }
180 };
181}
182
183impl From<&str> for ValueContainer {
184 #[inline]
185 fn from(value: &str) -> Self {
186 Self::Scalar(Value::String(value.to_string()))
187 }
188}
189
190impl<'a> From<Vec<&'a str>> for ValueContainer {
191 #[inline]
192 fn from(values: Vec<&'a str>) -> Self {
193 Self::Collection(MultiValues::from(values))
194 }
195}
196
197impl<'a, 'b> From<&'a [&'b str]> for ValueContainer {
198 #[inline]
199 fn from(values: &'a [&'b str]) -> Self {
200 Self::Collection(MultiValues::from(values))
201 }
202}
203
204impl<'a, 'b> From<&'a Vec<&'b str>> for ValueContainer {
205 #[inline]
206 fn from(values: &'a Vec<&'b str>) -> Self {
207 Self::Collection(MultiValues::from(values))
208 }
209}
210
211impl<'a, const N: usize> From<[&'a str; N]> for ValueContainer {
212 #[inline]
213 fn from(values: [&'a str; N]) -> Self {
214 Self::Collection(MultiValues::from(values))
215 }
216}
217
218impl<'a, 'b, const N: usize> From<&'a [&'b str; N]> for ValueContainer {
219 #[inline]
220 fn from(values: &'a [&'b str; N]) -> Self {
221 Self::Collection(MultiValues::from(values))
222 }
223}
224
225impl From<Value> for ValueContainer {
226 #[inline(always)]
227 fn from(value: Value) -> Self {
228 Self::Scalar(value)
229 }
230}
231
232impl From<MultiValues> for ValueContainer {
233 #[inline(always)]
234 fn from(values: MultiValues) -> Self {
235 Self::Collection(values)
236 }
237}
238
239impl ValueContainer {
240 /// Strictly borrows the scalar or first collection item without allocating.
241 #[must_use = "the borrowed first-value result should be handled"]
242 #[inline(always)]
243 pub fn get_first_ref<'a, T: ?Sized>(&'a self) -> ValueResult<&'a T>
244 where
245 &'a T: TryFrom<&'a Value, Error = ValueError> + TryFrom<&'a MultiValues, Error = ValueError>,
246 {
247 match self {
248 Self::Scalar(value) => <&'a T>::try_from(value),
249 Self::Collection(values) => <&'a T>::try_from(values),
250 }
251 }
252
253 /// Strictly borrows a scalar as a one-item slice or a complete collection.
254 #[must_use = "the borrowed collection result should be handled"]
255 #[inline(always)]
256 pub fn get_slice<'a, T>(&'a self) -> ValueResult<&'a [T]>
257 where
258 &'a T: TryFrom<&'a Value, Error = ValueError>,
259 &'a [T]: TryFrom<&'a MultiValues, Error = ValueError>,
260 {
261 match self {
262 Self::Scalar(value) => value.get_ref().map(std::slice::from_ref),
263 Self::Collection(values) => values.get_slice(),
264 }
265 }
266
267 /// Creates an unset scalar container for a declared data type.
268 ///
269 /// # Parameters
270 ///
271 /// * `data_type` - Declared scalar type while no value is set.
272 ///
273 /// # Returns
274 ///
275 /// A scalar `ValueContainer` with explicit unset storage.
276 #[inline(always)]
277 pub const fn new_unset_scalar(data_type: DataType) -> Self {
278 Self::Scalar(Value::new_unset(data_type))
279 }
280
281 /// Creates an unset collection container for a declared element type.
282 ///
283 /// # Parameters
284 ///
285 /// * `data_type` - Declared element type while no values are set.
286 ///
287 /// # Returns
288 ///
289 /// A collection `ValueContainer` with explicit unset storage.
290 #[inline(always)]
291 pub const fn new_unset_collection(data_type: DataType) -> Self {
292 Self::Collection(MultiValues::new_unset(data_type))
293 }
294
295 /// Returns the stored or declared data type.
296 ///
297 /// # Returns
298 ///
299 /// The scalar or collection element type, including the declared type of
300 /// unset storage.
301 #[must_use = "the runtime data type should be used"]
302 #[inline(always)]
303 pub fn data_type(&self) -> DataType {
304 match self {
305 Self::Scalar(value) => value.data_type(),
306 Self::Collection(values) => values.data_type(),
307 }
308 }
309
310 /// Returns whether this container has scalar shape.
311 ///
312 /// # Returns
313 ///
314 /// `true` for [`ValueContainer::Scalar`].
315 #[inline(always)]
316 #[must_use]
317 pub const fn is_scalar(&self) -> bool {
318 matches!(self, Self::Scalar(_))
319 }
320
321 /// Returns the contained scalar without consuming this container.
322 ///
323 /// # Returns
324 ///
325 /// `Some` for scalar storage, or `None` for collection storage.
326 #[must_use]
327 #[inline(always)]
328 pub const fn as_scalar(&self) -> Option<&Value> {
329 match self {
330 Self::Scalar(value) => Some(value),
331 Self::Collection(_) => None,
332 }
333 }
334
335 /// Consumes this container and returns its scalar value.
336 ///
337 /// # Returns
338 ///
339 /// The contained scalar value when this container has scalar shape.
340 ///
341 /// # Errors
342 ///
343 /// Returns the original container unchanged when it has collection shape.
344 #[inline(always)]
345 pub fn into_scalar(self) -> Result<Value, Self> {
346 match self {
347 Self::Scalar(value) => Ok(value),
348 Self::Collection(_) => Err(self),
349 }
350 }
351
352 /// Returns whether this container has collection shape.
353 ///
354 /// # Returns
355 ///
356 /// `true` for [`ValueContainer::Collection`].
357 #[inline(always)]
358 #[must_use]
359 pub const fn is_collection(&self) -> bool {
360 matches!(self, Self::Collection(_))
361 }
362
363 /// Returns the contained collection without consuming this container.
364 ///
365 /// # Returns
366 ///
367 /// `Some` for collection storage, or `None` for scalar storage.
368 #[must_use]
369 #[inline(always)]
370 pub const fn as_collection(&self) -> Option<&MultiValues> {
371 match self {
372 Self::Scalar(_) => None,
373 Self::Collection(values) => Some(values),
374 }
375 }
376
377 /// Consumes this container and returns its collection values.
378 ///
379 /// # Returns
380 ///
381 /// The contained values when this container has collection shape.
382 ///
383 /// # Errors
384 ///
385 /// Returns the original container unchanged when it has scalar shape.
386 #[inline(always)]
387 pub fn into_collection(self) -> Result<MultiValues, Self> {
388 match self {
389 Self::Scalar(_) => Err(self),
390 Self::Collection(values) => Ok(values),
391 }
392 }
393
394 /// Returns whether the shape contains no concrete value or collection.
395 ///
396 /// # Returns
397 ///
398 /// `true` when the contained scalar or collection is unset.
399 #[inline(always)]
400 #[must_use]
401 pub fn is_unset(&self) -> bool {
402 match self {
403 Self::Scalar(value) => value.is_unset(),
404 Self::Collection(values) => values.is_unset(),
405 }
406 }
407
408 /// Returns zero for unset storage, one for a concrete scalar, or the
409 /// concrete collection length.
410 ///
411 /// # Returns
412 ///
413 /// The number of concrete values represented by this container.
414 #[inline(always)]
415 #[must_use]
416 #[allow(clippy::len_without_is_empty)]
417 pub fn len(&self) -> usize {
418 match self {
419 Self::Scalar(value) => usize::from(!value.is_unset()),
420 Self::Collection(values) => values.len(),
421 }
422 }
423
424 /// Tests whether this container represents no concrete values.
425 ///
426 /// # Returns
427 ///
428 /// `true` for unset storage or a concrete empty collection.
429 #[inline(always)]
430 #[must_use]
431 pub fn is_empty(&self) -> bool {
432 self.len() == 0
433 }
434
435 /// Strictly reads a scalar or the first collection item as `T`.
436 ///
437 /// # Type Parameters
438 ///
439 /// * `T` - Strict target type.
440 ///
441 /// # Returns
442 ///
443 /// The scalar value or first collection item.
444 ///
445 /// # Errors
446 ///
447 /// Returns [`ValueError::Missing`] for unset or empty matching storage and
448 /// [`ValueError::TypeMismatch`] when the stored data type differs.
449 #[must_use = "the strict first-value result should be handled"]
450 #[inline(always)]
451 pub fn get_first<T>(&self) -> ValueResult<T>
452 where
453 T: StrictValueRead,
454 {
455 match self {
456 Self::Scalar(value) => T::read_scalar(value),
457 Self::Collection(values) => T::read_collection_first(values),
458 }
459 }
460
461 /// Strictly reads a scalar as a one-item list or all collection items.
462 ///
463 /// # Type Parameters
464 ///
465 /// * `T` - Strict target element type.
466 ///
467 /// # Returns
468 ///
469 /// A one-item scalar list or all collection items.
470 ///
471 /// # Errors
472 ///
473 /// Returns [`ValueError::Missing`] for unset matching storage and
474 /// [`ValueError::TypeMismatch`] when the stored data type differs.
475 #[must_use = "the strict collection read result should be handled"]
476 #[inline(always)]
477 pub fn get_list<T>(&self) -> ValueResult<Vec<T>>
478 where
479 T: StrictValueRead,
480 {
481 match self {
482 Self::Scalar(value) => T::read_scalar(value).map(|item| vec![item]),
483 Self::Collection(values) => T::read_collection_list(values),
484 }
485 }
486
487 /// Replaces this container, including its shape, from a supported input.
488 ///
489 /// # Type Parameters
490 ///
491 /// * `S` - Input type convertible into [`ValueContainer`].
492 ///
493 /// # Parameters
494 ///
495 /// * `value` - New scalar or collection value.
496 #[inline(always)]
497 pub fn set<S>(&mut self, value: S)
498 where
499 S: Into<Self>,
500 {
501 *self = value.into();
502 }
503
504 /// Appends values, promoting scalar storage to collection storage when the
505 /// input contains at least one concrete value. Same-typed empty and unset
506 /// input is a no-op.
507 ///
508 /// # Type Parameters
509 ///
510 /// * `S` - Input type convertible into [`ValueContainer`].
511 ///
512 /// # Parameters
513 ///
514 /// * `values` - Scalar or collection values to append.
515 ///
516 /// # Returns
517 ///
518 /// `Ok(())` after appending or accepting an empty same-typed input.
519 ///
520 /// # Errors
521 ///
522 /// Returns [`ValueError::TypeMismatch`] when the appended values have a
523 /// different data type.
524 pub fn add<S>(&mut self, values: S) -> ValueResult<()>
525 where
526 S: Into<Self>,
527 {
528 let other = values.into();
529 let expected = self.data_type();
530 let actual = other.data_type();
531 if expected != actual {
532 return Err(ValueError::TypeMismatch { expected, actual });
533 }
534 if other.is_empty() {
535 return Ok(());
536 }
537
538 match other {
539 Self::Scalar(value) => {
540 self.add_scalar(value, expected);
541 Ok(())
542 }
543 Self::Collection(other) => match self {
544 Self::Scalar(value) => {
545 let value = std::mem::replace(value, Value::new_unset(expected));
546 let mut collection = MultiValues::from(value);
547 collection.add(other)?;
548 *self = Self::Collection(collection);
549 Ok(())
550 }
551 Self::Collection(collection) => collection.add(other),
552 },
553 }
554 }
555
556 /// Removes concrete storage while preserving its shape and data type.
557 #[inline(always)]
558 pub fn unset(&mut self) {
559 match self {
560 Self::Scalar(value) => value.unset(),
561 Self::Collection(values) => values.unset(),
562 }
563 }
564
565 /// Converts a scalar or the first collection item to `T`.
566 ///
567 /// # Type Parameters
568 ///
569 /// * `T` - Target conversion type.
570 ///
571 /// # Returns
572 ///
573 /// The converted scalar or first collection item.
574 ///
575 /// # Errors
576 ///
577 /// Returns the mapped `qubit-datatype` conversion error.
578 #[cfg(feature = "converter")]
579 #[inline(always)]
580 pub fn to_first<T>(&self) -> ValueResult<T>
581 where
582 T: DataConversionTarget,
583 {
584 self.to_first_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
585 }
586
587 /// Converts a scalar or the first collection item using explicit policy and
588 /// limits.
589 ///
590 /// # Type Parameters
591 ///
592 /// * `T` - Target conversion type.
593 ///
594 /// # Parameters
595 ///
596 /// * `policy` - Conversion policy forwarded to the contained value.
597 /// * `limits` - Conversion limits forwarded to the contained value.
598 ///
599 /// # Returns
600 ///
601 /// The converted scalar or first collection item.
602 ///
603 /// # Errors
604 ///
605 /// Returns the mapped `qubit-datatype` conversion error.
606 #[cfg(feature = "converter")]
607 #[inline(always)]
608 pub fn to_first_with<T>(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<T>
609 where
610 T: DataConversionTarget,
611 {
612 match self {
613 Self::Scalar(value) => value.to_with(policy, limits),
614 Self::Collection(values) => values.to_first_with(policy, limits),
615 }
616 }
617
618 /// Converts the scalar or first collection item using an existing
619 /// conversion session.
620 ///
621 /// # Type Parameters
622 ///
623 /// * `T` - Target conversion type.
624 ///
625 /// # Parameters
626 ///
627 /// * `session` - Caller-owned session providing policy, limits, and budget.
628 ///
629 /// # Returns
630 ///
631 /// The converted scalar or first collection item.
632 ///
633 /// # Errors
634 ///
635 /// Returns the mapped missing, conversion, or budget error.
636 #[cfg(feature = "converter")]
637 #[inline(always)]
638 pub fn to_first_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<T>
639 where
640 T: DataConversionTarget,
641 {
642 match self {
643 Self::Scalar(value) => value.to_in(session),
644 Self::Collection(values) => values.to_first_in(session),
645 }
646 }
647
648 /// Converts a scalar to a list or converts every collection item.
649 ///
650 /// Scalar strings may be split according to collection conversion policy;
651 /// strings already stored in a collection are never split again.
652 ///
653 /// # Type Parameters
654 ///
655 /// * `T` - Target list element type.
656 ///
657 /// # Returns
658 ///
659 /// A converted scalar list or all converted collection items.
660 ///
661 /// # Errors
662 ///
663 /// Returns the mapped single-value or indexed list conversion error.
664 #[cfg(feature = "converter")]
665 #[inline(always)]
666 pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
667 where
668 T: DataConversionTarget,
669 {
670 self.to_list_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
671 }
672
673 /// Converts to a list using explicit conversion policy and limits.
674 ///
675 /// # Type Parameters
676 ///
677 /// * `T` - Target list element type.
678 ///
679 /// # Parameters
680 ///
681 /// * `policy` - Conversion policy forwarded to the contained value.
682 /// * `limits` - Conversion limits forwarded to the contained value.
683 ///
684 /// # Returns
685 ///
686 /// A converted scalar list or all converted collection items.
687 ///
688 /// # Errors
689 ///
690 /// Returns the mapped single-value or indexed list conversion error.
691 #[cfg(feature = "converter")]
692 pub fn to_list_with<T>(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<Vec<T>>
693 where
694 T: DataConversionTarget,
695 {
696 match self {
697 Self::Scalar(value) => match value.view() {
698 ValueRef::String(value) => ScalarStringDataConverters::from(value)
699 .to_vec_with(policy, limits)
700 .map_err(ValueError::from),
701 _ => value.to_with(policy, limits).map(|value| vec![value]),
702 },
703 Self::Collection(values) => values.to_list_with(policy, limits),
704 }
705 }
706
707 /// Converts this container to a list using an existing conversion session.
708 ///
709 /// # Type Parameters
710 ///
711 /// * `T` - Target list element type.
712 ///
713 /// # Parameters
714 ///
715 /// * `session` - Caller-owned session providing policy, limits, and budget.
716 ///
717 /// # Returns
718 ///
719 /// A converted scalar list or all converted collection items.
720 ///
721 /// # Errors
722 ///
723 /// Returns the mapped missing, conversion, indexed-list, or budget error.
724 #[cfg(feature = "converter")]
725 pub fn to_list_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<Vec<T>>
726 where
727 T: DataConversionTarget,
728 {
729 match self {
730 Self::Scalar(value) => match value.view() {
731 ValueRef::String(value) => ScalarStringDataConverters::from(value)
732 .to_vec_in(session)
733 .map_err(ValueError::from),
734 _ => value.to_in(session).map(|value| vec![value]),
735 },
736 Self::Collection(values) => values.to_list_in(session),
737 }
738 }
739
740 /// Appends a concrete scalar, promoting scalar storage to a collection.
741 ///
742 /// # Parameters
743 ///
744 /// * `value` - Concrete scalar to append.
745 /// * `data_type` - Shared runtime type of `self` and `value`.
746 ///
747 /// # Constraints
748 ///
749 /// Callers must ensure `value` is concrete and has `data_type`; the public
750 /// mutation entry points validate those invariants before calling this
751 /// helper.
752 #[inline]
753 fn add_scalar(&mut self, value: Value, data_type: DataType) {
754 match self {
755 Self::Scalar(current) => {
756 let current = std::mem::replace(current, Value::new_unset(data_type));
757 let collection = for_each_value_type!(value_container_pair_match, current, value);
758 *self = Self::Collection(collection);
759 }
760 Self::Collection(collection) => {
761 for_each_value_type!(value_container_push_match, collection, value);
762 }
763 }
764 }
765}
766
767#[cfg(all(feature = "converter", feature = "json"))]
768impl ValueContainer {
769 /// Projects this container while preserving concrete collection shape.
770 ///
771 /// Scalar storage uses the natural scalar projection; concrete collection
772 /// storage always uses a JSON array.
773 ///
774 /// # Returns
775 ///
776 /// The natural JSON representation, except scalar and collection unset
777 /// values both project to `null`.
778 ///
779 /// # Errors
780 ///
781 /// Returns the same structured projection error as the contained value.
782 #[inline(always)]
783 pub fn to_json_value(&self) -> ValueResult<serde_json::Value> {
784 self.to_json_value_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
785 }
786
787 /// Projects this container using explicit conversion policy and limits.
788 ///
789 /// # Parameters
790 ///
791 /// * `policy` - Controls duration units and precision-loss behavior.
792 /// * `limits` - Bounds conversion resource consumption.
793 ///
794 /// # Returns
795 ///
796 /// The natural JSON representation, except scalar and collection unset
797 /// values both project to `null`.
798 ///
799 /// # Errors
800 ///
801 /// Returns the same structured projection error as the contained value.
802 #[inline(always)]
803 pub fn to_json_value_with(
804 &self,
805 policy: &ConversionPolicy,
806 limits: &ConversionLimits,
807 ) -> ValueResult<serde_json::Value> {
808 crate::json::value_container_to_json_value_with(self, policy, limits)
809 }
810}