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
11use std::mem::replace;
12use std::slice::from_ref;
13
14#[cfg(feature = "converter")]
15use qubit_datatype::ConversionLimits;
16#[cfg(feature = "converter")]
17use qubit_datatype::ConversionPolicy;
18#[cfg(feature = "converter")]
19use qubit_datatype::ConversionSession;
20#[cfg(feature = "converter")]
21use qubit_datatype::DataConversionTarget;
22use qubit_datatype::DataType;
23#[cfg(feature = "converter")]
24use qubit_datatype::ScalarStringDataConverters;
25#[cfg(all(feature = "converter", feature = "json"))]
26use serde_json::Value as JsonValue;
27
28use crate::MultiValues;
29use crate::StrictValueRead;
30use crate::Value;
31use crate::ValueError;
32use crate::ValueResult;
33use crate::multi_values::MultiValuesRepr;
34#[cfg(feature = "converter")]
35use crate::value::ValueRef;
36use crate::value::ValueRepr;
37
38/// A typed value whose scalar or collection shape is explicit.
39///
40/// The shape is never inferred from collection length. In particular,
41/// `Scalar(Value::Int32(42))` and
42/// `Collection(MultiValues::Int32(vec![42]))` remain distinguishable through
43/// conversion and serialization boundaries.
44///
45/// # Examples
46///
47/// ```
48/// use qubit_value::ValueContainer;
49///
50/// let scalar = ValueContainer::from(42_i32);
51/// let collection = ValueContainer::from(vec![42_i32]);
52/// assert!(scalar.is_scalar());
53/// assert!(collection.is_collection());
54/// ```
55#[must_use]
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57pub enum ValueContainer {
58 /// One typed value.
59 Scalar(
60 /// Stored scalar value.
61 Value,
62 ),
63 /// A homogeneous typed collection.
64 Collection(
65 /// Stored homogeneous collection.
66 MultiValues,
67 ),
68}
69
70/// Implements scalar and collection conversions from the shared value table.
71macro_rules! impl_value_container_from_table {
72 (
73 ;
74 $(
75 (
76 [$($cfg:meta),*],
77 $variant:ident,
78 $type:ty,
79 $data_type:expr,
80 $materialization:ident,
81 $json_class:ident,
82 $number_projection:ident,
83 $value_doc:literal,
84 $multi_doc:literal
85 $(, $_wire:tt)*
86 )
87 ),+ $(,)?
88 ) => {
89 $(
90 $(#[$cfg])*
91 $(#[cfg_attr(docsrs, doc($cfg))])*
92 impl From<$type> for ValueContainer {
93 /// Creates a scalar container by taking ownership of `value`.
94 ///
95 /// # Parameters
96 ///
97 /// * `value` - Input converted with the ownership behavior described above.
98 ///
99 /// # Returns
100 ///
101 /// A container preserving the input data type and scalar or collection shape.
102 #[inline(always)]
103 fn from(value: $type) -> Self {
104 Self::Scalar(Value::$variant(value))
105 }
106 }
107
108 $(#[$cfg])*
109 $(#[cfg_attr(docsrs, doc($cfg))])*
110 impl From<Vec<$type>> for ValueContainer {
111 /// Takes ownership of the vector and its element storage without cloning elements.
112 /// An empty vector creates a concrete empty collection.
113 ///
114 /// # Parameters
115 ///
116 /// * `values` - Input converted with the ownership behavior described above.
117 ///
118 /// # Returns
119 ///
120 /// A container preserving the input data type and scalar or collection shape.
121 #[inline(always)]
122 fn from(values: Vec<$type>) -> Self {
123 Self::Collection(MultiValues::$variant(values))
124 }
125 }
126
127 $(#[$cfg])*
128 $(#[cfg_attr(docsrs, doc($cfg))])*
129 impl From<&[$type]> for ValueContainer {
130 /// Clones the elements into an owned collection, preserving order.
131 /// An empty slice creates a concrete empty collection.
132 ///
133 /// # Parameters
134 ///
135 /// * `values` - Input converted with the ownership behavior described above.
136 ///
137 /// # Returns
138 ///
139 /// A container preserving the input data type and scalar or collection shape.
140 #[inline]
141 fn from(values: &[$type]) -> Self {
142 Self::Collection(MultiValues::$variant(values.to_vec()))
143 }
144 }
145
146 $(#[$cfg])*
147 $(#[cfg_attr(docsrs, doc($cfg))])*
148 impl From<&Vec<$type>> for ValueContainer {
149 /// Clones the elements into an owned collection, preserving order.
150 /// An empty vector creates a concrete empty collection.
151 ///
152 /// # Parameters
153 ///
154 /// * `values` - Input converted with the ownership behavior described above.
155 ///
156 /// # Returns
157 ///
158 /// A container preserving the input data type and scalar or collection shape.
159 #[inline]
160 fn from(values: &Vec<$type>) -> Self {
161 Self::Collection(MultiValues::$variant(values.clone()))
162 }
163 }
164
165 $(#[$cfg])*
166 $(#[cfg_attr(docsrs, doc($cfg))])*
167 impl<const N: usize> From<[$type; N]> for ValueContainer {
168 /// Moves the array elements into a newly allocated vector, preserving order.
169 /// An empty array creates a concrete empty collection.
170 ///
171 /// # Type Parameters
172 ///
173 /// * `N` - Number of input array elements.
174 ///
175 /// # Parameters
176 ///
177 /// * `values` - Input converted with the ownership behavior described above.
178 ///
179 /// # Returns
180 ///
181 /// A container preserving the input data type and scalar or collection shape.
182 #[inline]
183 fn from(values: [$type; N]) -> Self {
184 Self::Collection(MultiValues::$variant(Vec::from(values)))
185 }
186 }
187
188 $(#[$cfg])*
189 $(#[cfg_attr(docsrs, doc($cfg))])*
190 impl<const N: usize> From<&[$type; N]> for ValueContainer {
191 /// Clones the elements into an owned collection, preserving order.
192 /// An empty array creates a concrete empty collection.
193 ///
194 /// # Type Parameters
195 ///
196 /// * `N` - Number of input array elements.
197 ///
198 /// # Parameters
199 ///
200 /// * `values` - Input converted with the ownership behavior described above.
201 ///
202 /// # Returns
203 ///
204 /// A container preserving the input data type and scalar or collection shape.
205 #[inline]
206 fn from(values: &[$type; N]) -> Self {
207 Self::Collection(MultiValues::$variant(values.to_vec()))
208 }
209 }
210 )+
211 };
212}
213
214for_each_value_type!(impl_value_container_from_table);
215
216/// Builds a typed collection from one or two same-typed scalar values.
217macro_rules! value_container_pair_match {
218 ($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)*)),+ $(,)?) => {
219 match ($first.repr, $second.repr) {
220 $(
221 $(#[$cfg])*
222 (ValueRepr::$variant(first), ValueRepr::$variant(second)) => {
223 MultiValues::$variant(vec![
224 value_storage_into_multi!($variant, first),
225 value_storage_into_multi!($variant, second),
226 ])
227 }
228 )+
229 $(
230 $(#[$cfg])*
231 (ValueRepr::Unset(_), ValueRepr::$variant(second)) => {
232 MultiValues::$variant(vec![value_storage_into_multi!($variant, second)])
233 }
234 )+
235 _ => unreachable!(),
236 }
237 };
238}
239
240/// Pushes a same-typed scalar directly into collection storage.
241macro_rules! value_container_push_match {
242 ($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)*)),+ $(,)?) => {
243 match (&mut $collection.repr, $value.repr) {
244 $(
245 $(#[$cfg])*
246 (MultiValuesRepr::$variant(values), ValueRepr::$variant(value)) => {
247 values.push(value_storage_into_multi!($variant, value))
248 },
249 )+
250 $(
251 $(#[$cfg])*
252 (slot @ MultiValuesRepr::Unset(_), ValueRepr::$variant(value)) => {
253 *slot = MultiValuesRepr::$variant(vec![value_storage_into_multi!($variant, value)]);
254 }
255 )+
256 _ => unreachable!(),
257 }
258 };
259}
260
261impl From<&str> for ValueContainer {
262 /// Copies the text into an owned scalar string.
263 ///
264 /// # Parameters
265 ///
266 /// * `value` - Input converted with the ownership behavior described above.
267 ///
268 /// # Returns
269 ///
270 /// A container preserving the input data type and scalar or collection
271 /// shape.
272 #[inline]
273 fn from(value: &str) -> Self {
274 Self::Scalar(Value::String(value.to_string()))
275 }
276}
277
278impl<'a> From<Vec<&'a str>> for ValueContainer {
279 /// Copies each string into an owned collection, preserving order.
280 /// An empty vector creates a concrete empty collection.
281 ///
282 /// # Parameters
283 ///
284 /// * `values` - Input converted with the ownership behavior described
285 /// above.
286 ///
287 /// # Returns
288 ///
289 /// A container preserving the input data type and scalar or collection
290 /// shape.
291 #[inline]
292 fn from(values: Vec<&'a str>) -> Self {
293 Self::Collection(MultiValues::from(values))
294 }
295}
296
297impl<'a, 'b> From<&'a [&'b str]> for ValueContainer {
298 /// Copies each string into an owned collection, preserving order.
299 /// An empty slice creates a concrete empty collection.
300 ///
301 /// # Parameters
302 ///
303 /// * `values` - Input converted with the ownership behavior described
304 /// above.
305 ///
306 /// # Returns
307 ///
308 /// A container preserving the input data type and scalar or collection
309 /// shape.
310 #[inline]
311 fn from(values: &'a [&'b str]) -> Self {
312 Self::Collection(MultiValues::from(values))
313 }
314}
315
316impl<'a, 'b> From<&'a Vec<&'b str>> for ValueContainer {
317 /// Copies each string into an owned collection, preserving order.
318 /// An empty vector creates a concrete empty collection.
319 ///
320 /// # Parameters
321 ///
322 /// * `values` - Input converted with the ownership behavior described
323 /// above.
324 ///
325 /// # Returns
326 ///
327 /// A container preserving the input data type and scalar or collection
328 /// shape.
329 #[inline]
330 fn from(values: &'a Vec<&'b str>) -> Self {
331 Self::Collection(MultiValues::from(values))
332 }
333}
334
335impl<'a, const N: usize> From<[&'a str; N]> for ValueContainer {
336 /// Copies each string into an owned collection, preserving order.
337 /// An empty array creates a concrete empty collection.
338 ///
339 /// # Type Parameters
340 ///
341 /// * `N` - Number of input array elements.
342 ///
343 /// # Parameters
344 ///
345 /// * `values` - Input converted with the ownership behavior described
346 /// above.
347 ///
348 /// # Returns
349 ///
350 /// A container preserving the input data type and scalar or collection
351 /// shape.
352 #[inline]
353 fn from(values: [&'a str; N]) -> Self {
354 Self::Collection(MultiValues::from(values))
355 }
356}
357
358impl<'a, 'b, const N: usize> From<&'a [&'b str; N]> for ValueContainer {
359 /// Copies each string into an owned collection, preserving order.
360 /// An empty array creates a concrete empty collection.
361 ///
362 /// # Type Parameters
363 ///
364 /// * `N` - Number of input array elements.
365 ///
366 /// # Parameters
367 ///
368 /// * `values` - Input converted with the ownership behavior described
369 /// above.
370 ///
371 /// # Returns
372 ///
373 /// A container preserving the input data type and scalar or collection
374 /// shape.
375 #[inline]
376 fn from(values: &'a [&'b str; N]) -> Self {
377 Self::Collection(MultiValues::from(values))
378 }
379}
380
381impl From<Value> for ValueContainer {
382 /// Wraps the value as a scalar without cloning it.
383 ///
384 /// # Parameters
385 ///
386 /// * `value` - Input converted with the ownership behavior described above.
387 ///
388 /// # Returns
389 ///
390 /// A container preserving the input data type and scalar or collection
391 /// shape.
392 #[inline]
393 fn from(value: Value) -> Self {
394 Self::Scalar(value)
395 }
396}
397
398impl From<MultiValues> for ValueContainer {
399 /// Wraps the collection without cloning its elements.
400 ///
401 /// # Parameters
402 ///
403 /// * `values` - Input converted with the ownership behavior described
404 /// above.
405 ///
406 /// # Returns
407 ///
408 /// A container preserving the input data type and scalar or collection
409 /// shape.
410 #[inline]
411 fn from(values: MultiValues) -> Self {
412 Self::Collection(values)
413 }
414}
415
416impl ValueContainer {
417 /// Creates an unset scalar container for a declared data type.
418 ///
419 /// # Parameters
420 ///
421 /// * `data_type` - Declared scalar type while no value is set.
422 ///
423 /// # Returns
424 ///
425 /// A scalar `ValueContainer` with explicit unset storage.
426 #[inline]
427 pub const fn new_unset_scalar(data_type: DataType) -> Self {
428 Self::Scalar(Value::new_unset(data_type))
429 }
430
431 /// Creates an unset collection container for a declared element type.
432 ///
433 /// # Parameters
434 ///
435 /// * `data_type` - Declared element type while no values are set.
436 ///
437 /// # Returns
438 ///
439 /// A collection `ValueContainer` with explicit unset storage.
440 #[inline]
441 pub const fn new_unset_collection(data_type: DataType) -> Self {
442 Self::Collection(MultiValues::new_unset(data_type))
443 }
444
445 /// Strictly borrows the scalar or first collection item without allocating.
446 ///
447 /// The returned reference is tied to this container's lifetime. The
448 /// requested type must match the scalar or collection element type.
449 ///
450 /// # Type Parameters
451 ///
452 /// * `T` - Stored scalar or element type to borrow.
453 ///
454 /// # Returns
455 ///
456 /// A reference to the scalar or the first collection item.
457 ///
458 /// # Errors
459 ///
460 /// Returns [`ValueError::Missing`] when no item is available, or a type
461 /// conversion error when the stored type does not match `T`.
462 ///
463 /// # Examples
464 ///
465 /// ```
466 /// use qubit_value::ValueContainer;
467 /// use qubit_value::ValueError;
468 ///
469 /// let values = ValueContainer::from(vec![String::from("a"), String::from("b")]);
470 /// let first: &String = values.get_first_ref().unwrap();
471 /// assert_eq!(first, "a");
472 /// let empty = ValueContainer::from(Vec::<String>::new());
473 /// assert!(matches!(empty.get_first_ref::<String>(), Err(ValueError::Missing(_))));
474 /// ```
475 #[inline(always)]
476 pub fn get_first_ref<'a, T: ?Sized>(&'a self) -> ValueResult<&'a T>
477 where
478 &'a T:
479 TryFrom<&'a Value, Error = ValueError> + TryFrom<&'a MultiValues, Error = ValueError>,
480 {
481 match self {
482 Self::Scalar(value) => <&'a T>::try_from(value),
483 Self::Collection(values) => <&'a T>::try_from(values),
484 }
485 }
486
487 /// Strictly borrows a scalar as a one-item slice or a complete collection.
488 ///
489 /// The returned slice is tied to this container's lifetime. The requested
490 /// element type must match the stored type.
491 ///
492 /// # Type Parameters
493 ///
494 /// * `T` - Stored element type of the borrowed slice.
495 ///
496 /// # Returns
497 ///
498 /// A one-item slice for a scalar, or all collection items. An empty
499 /// collection returns an empty slice.
500 ///
501 /// # Errors
502 ///
503 /// Returns [`ValueError::Missing`] when an unset scalar or collection has
504 /// the requested type, or [`ValueError::TypeMismatch`] when the stored type
505 /// does not match `T`. A concrete empty collection returns an empty slice.
506 ///
507 /// # Examples
508 ///
509 /// ```
510 /// use qubit_datatype::DataType;
511 ///
512 /// use qubit_value::ValueContainer;
513 /// use qubit_value::ValueError;
514 ///
515 /// let values = ValueContainer::from(vec![String::from("a"), String::from("b")]);
516 /// let all: &[String] = values.get_slice().unwrap();
517 /// assert_eq!(all.len(), 2);
518 /// let empty = ValueContainer::from(Vec::<String>::new());
519 /// assert!(empty.get_slice::<String>().unwrap().is_empty());
520 /// let unset_scalar = ValueContainer::new_unset_scalar(DataType::String);
521 /// let unset_collection = ValueContainer::new_unset_collection(DataType::String);
522 /// assert!(matches!(unset_scalar.get_slice::<String>(), Err(ValueError::Missing(_))));
523 /// assert!(matches!(unset_collection.get_slice::<String>(), Err(ValueError::Missing(_))));
524 /// ```
525 #[inline(always)]
526 pub fn get_slice<'a, T>(&'a self) -> ValueResult<&'a [T]>
527 where
528 &'a T: TryFrom<&'a Value, Error = ValueError>,
529 &'a [T]: TryFrom<&'a MultiValues, Error = ValueError>,
530 {
531 match self {
532 Self::Scalar(value) => value.get_ref().map(from_ref),
533 Self::Collection(values) => values.get_slice(),
534 }
535 }
536
537 /// Returns the stored or declared data type.
538 ///
539 /// # Returns
540 ///
541 /// The scalar or collection element type, including the declared type of
542 /// unset storage.
543 #[inline]
544 pub fn data_type(&self) -> DataType {
545 match self {
546 Self::Scalar(value) => value.data_type(),
547 Self::Collection(values) => values.data_type(),
548 }
549 }
550
551 /// Returns whether this container has scalar shape.
552 ///
553 /// # Returns
554 ///
555 /// `true` for [`ValueContainer::Scalar`].
556 #[inline]
557 #[must_use]
558 pub const fn is_scalar(&self) -> bool {
559 matches!(self, Self::Scalar(_))
560 }
561
562 /// Returns the contained scalar without consuming this container.
563 ///
564 /// # Returns
565 ///
566 /// `Some` for scalar storage, or `None` for collection storage.
567 #[must_use]
568 #[inline]
569 pub const fn as_scalar(&self) -> Option<&Value> {
570 match self {
571 Self::Scalar(value) => Some(value),
572 Self::Collection(_) => None,
573 }
574 }
575
576 /// Returns whether this container has collection shape.
577 ///
578 /// # Returns
579 ///
580 /// `true` for [`ValueContainer::Collection`].
581 #[inline]
582 #[must_use]
583 pub const fn is_collection(&self) -> bool {
584 matches!(self, Self::Collection(_))
585 }
586
587 /// Returns the contained collection without consuming this container.
588 ///
589 /// # Returns
590 ///
591 /// `Some` for collection storage, or `None` for scalar storage.
592 #[must_use]
593 #[inline]
594 pub const fn as_collection(&self) -> Option<&MultiValues> {
595 match self {
596 Self::Scalar(_) => None,
597 Self::Collection(values) => Some(values),
598 }
599 }
600
601 /// Returns whether the shape contains no concrete value or collection.
602 ///
603 /// # Returns
604 ///
605 /// `true` when the contained scalar or collection is unset.
606 #[inline(always)]
607 #[must_use]
608 pub fn is_unset(&self) -> bool {
609 match self {
610 Self::Scalar(value) => value.is_unset(),
611 Self::Collection(values) => values.is_unset(),
612 }
613 }
614
615 /// Returns zero for unset storage, one for a concrete scalar, or the
616 /// concrete collection length.
617 ///
618 /// # Returns
619 ///
620 /// The number of concrete values represented by this container.
621 #[inline]
622 #[must_use]
623 #[allow(clippy::len_without_is_empty)]
624 pub fn len(&self) -> usize {
625 match self {
626 Self::Scalar(value) => usize::from(!value.is_unset()),
627 Self::Collection(values) => values.len(),
628 }
629 }
630
631 /// Tests whether this container represents no concrete values.
632 ///
633 /// # Returns
634 ///
635 /// `true` for unset storage or a concrete empty collection.
636 #[inline]
637 #[must_use]
638 pub fn is_empty(&self) -> bool {
639 self.len() == 0
640 }
641
642 /// Strictly reads a scalar or the first collection item as `T`.
643 ///
644 /// # Type Parameters
645 ///
646 /// * `T` - Strict target type.
647 ///
648 /// # Returns
649 ///
650 /// The scalar value or first collection item.
651 ///
652 /// # Errors
653 ///
654 /// Returns [`ValueError::Missing`] for unset or empty matching storage and
655 /// [`ValueError::TypeMismatch`] when the stored data type differs.
656 #[inline(always)]
657 pub fn get_first<T>(&self) -> ValueResult<T>
658 where
659 T: StrictValueRead,
660 {
661 match self {
662 Self::Scalar(value) => T::read_scalar(value),
663 Self::Collection(values) => T::read_collection_first(values),
664 }
665 }
666
667 /// Strictly reads a scalar as a one-item list or all collection items.
668 ///
669 /// # Type Parameters
670 ///
671 /// * `T` - Strict target element type.
672 ///
673 /// # Returns
674 ///
675 /// A one-item scalar list or all collection items.
676 ///
677 /// # Errors
678 ///
679 /// Returns [`ValueError::Missing`] for unset matching storage and
680 /// [`ValueError::TypeMismatch`] when the stored data type differs.
681 #[inline(always)]
682 pub fn get_list<T>(&self) -> ValueResult<Vec<T>>
683 where
684 T: StrictValueRead,
685 {
686 match self {
687 Self::Scalar(value) => T::read_scalar(value).map(|item| vec![item]),
688 Self::Collection(values) => T::read_collection_list(values),
689 }
690 }
691
692 /// Converts a scalar or the first collection item to `T`.
693 ///
694 /// # Type Parameters
695 ///
696 /// * `T` - Target conversion type.
697 ///
698 /// # Returns
699 ///
700 /// The converted scalar or first collection item.
701 ///
702 /// # Errors
703 ///
704 /// Returns the mapped `qubit-datatype` conversion error.
705 #[cfg(feature = "converter")]
706 #[inline(always)]
707 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
708 pub fn to_first<T>(&self) -> ValueResult<T>
709 where
710 T: DataConversionTarget,
711 {
712 self.to_first_with(
713 ConversionPolicy::default_ref(),
714 ConversionLimits::default_ref(),
715 )
716 }
717
718 /// Converts a scalar or the first collection item using explicit policy and
719 /// limits.
720 ///
721 /// # Type Parameters
722 ///
723 /// * `T` - Target conversion type.
724 ///
725 /// # Parameters
726 ///
727 /// * `policy` - Conversion policy forwarded to the contained value.
728 /// * `limits` - Conversion limits forwarded to the contained value.
729 ///
730 /// # Returns
731 ///
732 /// The converted scalar or first collection item.
733 ///
734 /// # Errors
735 ///
736 /// Returns the mapped `qubit-datatype` conversion error.
737 #[cfg(feature = "converter")]
738 #[inline(always)]
739 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
740 pub fn to_first_with<T>(
741 &self,
742 policy: &ConversionPolicy,
743 limits: &ConversionLimits,
744 ) -> ValueResult<T>
745 where
746 T: DataConversionTarget,
747 {
748 match self {
749 Self::Scalar(value) => value.to_with(policy, limits),
750 Self::Collection(values) => values.to_first_with(policy, limits),
751 }
752 }
753
754 /// Converts the scalar or first collection item using an existing
755 /// conversion session.
756 ///
757 /// # Type Parameters
758 ///
759 /// * `T` - Target conversion type.
760 ///
761 /// # Parameters
762 ///
763 /// * `session` - Caller-owned session providing policy, limits, and budget.
764 ///
765 /// # Returns
766 ///
767 /// The converted scalar or first collection item.
768 ///
769 /// # Errors
770 ///
771 /// Returns the mapped missing, conversion, or budget error.
772 #[cfg(feature = "converter")]
773 #[inline(always)]
774 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
775 pub fn to_first_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<T>
776 where
777 T: DataConversionTarget,
778 {
779 match self {
780 Self::Scalar(value) => value.to_in(session),
781 Self::Collection(values) => values.to_first_in(session),
782 }
783 }
784
785 /// Converts a scalar to a list or converts every collection item.
786 ///
787 /// Scalar strings may be split according to collection conversion policy;
788 /// strings already stored in a collection are never split again.
789 ///
790 /// # Type Parameters
791 ///
792 /// * `T` - Target list element type.
793 ///
794 /// # Returns
795 ///
796 /// A converted scalar list or all converted collection items.
797 ///
798 /// # Errors
799 ///
800 /// Returns the mapped single-value or indexed list conversion error.
801 #[cfg(feature = "converter")]
802 #[inline(always)]
803 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
804 pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
805 where
806 T: DataConversionTarget,
807 {
808 self.to_list_with(
809 ConversionPolicy::default_ref(),
810 ConversionLimits::default_ref(),
811 )
812 }
813
814 /// Converts to a list using explicit conversion policy and limits.
815 ///
816 /// # Type Parameters
817 ///
818 /// * `T` - Target list element type.
819 ///
820 /// # Parameters
821 ///
822 /// * `policy` - Conversion policy forwarded to the contained value.
823 /// * `limits` - Conversion limits forwarded to the contained value.
824 ///
825 /// # Returns
826 ///
827 /// A converted scalar list or all converted collection items.
828 ///
829 /// # Errors
830 ///
831 /// Returns the mapped single-value or indexed list conversion error.
832 #[cfg(feature = "converter")]
833 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
834 pub fn to_list_with<T>(
835 &self,
836 policy: &ConversionPolicy,
837 limits: &ConversionLimits,
838 ) -> ValueResult<Vec<T>>
839 where
840 T: DataConversionTarget,
841 {
842 match self {
843 Self::Scalar(value) => match value.view() {
844 ValueRef::String(value) => ScalarStringDataConverters::from(value)
845 .to_vec_with(policy, limits)
846 .map_err(ValueError::from),
847 _ => value.to_with(policy, limits).map(|value| vec![value]),
848 },
849 Self::Collection(values) => values.to_list_with(policy, limits),
850 }
851 }
852
853 /// Converts this container to a list using an existing conversion session.
854 ///
855 /// # Type Parameters
856 ///
857 /// * `T` - Target list element type.
858 ///
859 /// # Parameters
860 ///
861 /// * `session` - Caller-owned session providing policy, limits, and budget.
862 ///
863 /// # Returns
864 ///
865 /// A converted scalar list or all converted collection items.
866 ///
867 /// # Errors
868 ///
869 /// Returns the mapped missing, conversion, indexed-list, or budget error.
870 #[cfg(feature = "converter")]
871 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
872 pub fn to_list_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<Vec<T>>
873 where
874 T: DataConversionTarget,
875 {
876 match self {
877 Self::Scalar(value) => match value.view() {
878 ValueRef::String(value) => ScalarStringDataConverters::from(value)
879 .to_vec_in(session)
880 .map_err(ValueError::from),
881 _ => value.to_in(session).map(|value| vec![value]),
882 },
883 Self::Collection(values) => values.to_list_in(session),
884 }
885 }
886
887 /// Consumes this container and returns its scalar value.
888 ///
889 /// # Returns
890 ///
891 /// The contained scalar value when this container has scalar shape.
892 ///
893 /// # Errors
894 ///
895 /// Returns the original container unchanged when it has collection shape.
896 #[inline(always)]
897 pub fn into_scalar(self) -> Result<Value, Self> {
898 match self {
899 Self::Scalar(value) => Ok(value),
900 Self::Collection(_) => Err(self),
901 }
902 }
903
904 /// Consumes this container and returns its collection values.
905 ///
906 /// # Returns
907 ///
908 /// The contained values when this container has collection shape.
909 ///
910 /// # Errors
911 ///
912 /// Returns the original container unchanged when it has scalar shape.
913 #[inline(always)]
914 pub fn into_collection(self) -> Result<MultiValues, Self> {
915 match self {
916 Self::Scalar(_) => Err(self),
917 Self::Collection(values) => Ok(values),
918 }
919 }
920
921 /// Replaces this container, including its shape, from a supported input.
922 ///
923 /// # Type Parameters
924 ///
925 /// * `S` - Input type convertible into [`ValueContainer`].
926 ///
927 /// # Parameters
928 ///
929 /// * `value` - New scalar or collection value.
930 #[inline(always)]
931 pub fn set<S>(&mut self, value: S)
932 where
933 S: Into<Self>,
934 {
935 *self = value.into();
936 }
937
938 /// Appends values, promoting scalar storage to collection storage when the
939 /// input contains at least one concrete value. Same-typed empty and unset
940 /// input is a no-op.
941 ///
942 /// # Type Parameters
943 ///
944 /// * `S` - Input type convertible into [`ValueContainer`].
945 ///
946 /// # Parameters
947 ///
948 /// * `values` - Scalar or collection values to append.
949 ///
950 /// # Returns
951 ///
952 /// `Ok(())` after appending or accepting an empty same-typed input.
953 ///
954 /// # Errors
955 ///
956 /// Returns [`ValueError::TypeMismatch`] when the appended values have a
957 /// different data type.
958 pub fn add<S>(&mut self, values: S) -> ValueResult<()>
959 where
960 S: Into<Self>,
961 {
962 let other = values.into();
963 let expected = self.data_type();
964 let actual = other.data_type();
965 if expected != actual {
966 return Err(ValueError::TypeMismatch { expected, actual });
967 }
968 if other.is_empty() {
969 return Ok(());
970 }
971
972 match other {
973 Self::Scalar(value) => {
974 self.add_scalar(value, expected);
975 Ok(())
976 }
977 Self::Collection(other) => match self {
978 Self::Scalar(value) => {
979 let value = replace(value, Value::new_unset(expected));
980 let mut collection = MultiValues::from(value);
981 collection.add(other)?;
982 *self = Self::Collection(collection);
983 Ok(())
984 }
985 Self::Collection(collection) => collection.add(other),
986 },
987 }
988 }
989
990 /// Removes concrete storage while preserving its shape and data type.
991 #[inline(always)]
992 pub fn unset(&mut self) {
993 match self {
994 Self::Scalar(value) => value.unset(),
995 Self::Collection(values) => values.unset(),
996 }
997 }
998
999 /// Appends a concrete scalar, promoting scalar storage to a collection.
1000 ///
1001 /// # Parameters
1002 ///
1003 /// * `value` - Concrete scalar to append.
1004 /// * `data_type` - Shared runtime type of `self` and `value`.
1005 ///
1006 /// # Constraints
1007 ///
1008 /// Callers must ensure `value` is concrete and has `data_type`; the public
1009 /// mutation entry points validate those invariants before calling this
1010 /// helper.
1011 #[inline]
1012 fn add_scalar(&mut self, value: Value, data_type: DataType) {
1013 match self {
1014 Self::Scalar(current) => {
1015 let current = replace(current, Value::new_unset(data_type));
1016 let collection = for_each_value_type!(value_container_pair_match, current, value);
1017 *self = Self::Collection(collection);
1018 }
1019 Self::Collection(collection) => {
1020 for_each_value_type!(value_container_push_match, collection, value);
1021 }
1022 }
1023 }
1024}
1025
1026#[cfg(all(feature = "converter", feature = "json"))]
1027#[cfg_attr(docsrs, doc(cfg(all(feature = "converter", feature = "json"))))]
1028impl ValueContainer {
1029 /// Projects this container while preserving concrete collection shape.
1030 ///
1031 /// Scalar storage uses the natural scalar projection; concrete collection
1032 /// storage always uses a JSON array.
1033 ///
1034 /// # Returns
1035 ///
1036 /// The natural JSON representation, except scalar and collection unset
1037 /// values both project to `null`.
1038 ///
1039 /// For natural JSON projection, `max_text_bytes` bounds each string value
1040 /// and each formatted number or rich-value text by its UTF-8 byte length,
1041 /// including strings inside materialized JSON. Object keys are excluded
1042 /// from this per-item bound but still consume applicable key and
1043 /// cumulative input, output, and payload budgets. This is neither a
1044 /// cumulative text limit nor a limit on escaped JSON encoding size.
1045 ///
1046 /// # Errors
1047 ///
1048 /// Returns the same structured projection error as the contained value.
1049 #[inline(always)]
1050 pub fn to_json_value(&self) -> ValueResult<JsonValue> {
1051 self.to_json_value_with(
1052 ConversionPolicy::default_ref(),
1053 ConversionLimits::default_ref(),
1054 )
1055 }
1056
1057 /// Projects this container using explicit conversion policy and limits.
1058 ///
1059 /// # Parameters
1060 ///
1061 /// * `policy` - Controls duration units and precision-loss behavior.
1062 /// * `limits` - Bounds conversion resource consumption.
1063 ///
1064 /// # Returns
1065 ///
1066 /// The natural JSON representation, except scalar and collection unset
1067 /// values both project to `null`.
1068 ///
1069 /// # Errors
1070 ///
1071 /// Returns the same structured projection error as the contained value.
1072 #[inline]
1073 pub fn to_json_value_with(
1074 &self,
1075 policy: &ConversionPolicy,
1076 limits: &ConversionLimits,
1077 ) -> ValueResult<JsonValue> {
1078 crate::json::value_container_to_json_value_with(self, policy, limits)
1079 }
1080}