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: TryFrom<&'a Value, Error = ValueError> + TryFrom<&'a MultiValues, Error = ValueError>,
479 {
480 match self {
481 Self::Scalar(value) => <&'a T>::try_from(value),
482 Self::Collection(values) => <&'a T>::try_from(values),
483 }
484 }
485
486 /// Strictly borrows a scalar as a one-item slice or a complete collection.
487 ///
488 /// The returned slice is tied to this container's lifetime. The requested
489 /// element type must match the stored type.
490 ///
491 /// # Type Parameters
492 ///
493 /// * `T` - Stored element type of the borrowed slice.
494 ///
495 /// # Returns
496 ///
497 /// A one-item slice for a scalar, or all collection items. An empty
498 /// collection returns an empty slice.
499 ///
500 /// # Errors
501 ///
502 /// Returns [`ValueError::Missing`] when an unset scalar or collection has
503 /// the requested type, or [`ValueError::TypeMismatch`] when the stored type
504 /// does not match `T`. A concrete empty collection returns an empty slice.
505 ///
506 /// # Examples
507 ///
508 /// ```
509 /// use qubit_datatype::DataType;
510 ///
511 /// use qubit_value::ValueContainer;
512 /// use qubit_value::ValueError;
513 ///
514 /// let values = ValueContainer::from(vec![String::from("a"), String::from("b")]);
515 /// let all: &[String] = values.get_slice().unwrap();
516 /// assert_eq!(all.len(), 2);
517 /// let empty = ValueContainer::from(Vec::<String>::new());
518 /// assert!(empty.get_slice::<String>().unwrap().is_empty());
519 /// let unset_scalar = ValueContainer::new_unset_scalar(DataType::String);
520 /// let unset_collection = ValueContainer::new_unset_collection(DataType::String);
521 /// assert!(matches!(unset_scalar.get_slice::<String>(), Err(ValueError::Missing(_))));
522 /// assert!(matches!(unset_collection.get_slice::<String>(), Err(ValueError::Missing(_))));
523 /// ```
524 #[inline(always)]
525 pub fn get_slice<'a, T>(&'a self) -> ValueResult<&'a [T]>
526 where
527 &'a T: TryFrom<&'a Value, Error = ValueError>,
528 &'a [T]: TryFrom<&'a MultiValues, Error = ValueError>,
529 {
530 match self {
531 Self::Scalar(value) => value.get_ref().map(from_ref),
532 Self::Collection(values) => values.get_slice(),
533 }
534 }
535
536 /// Returns the stored or declared data type.
537 ///
538 /// # Returns
539 ///
540 /// The scalar or collection element type, including the declared type of
541 /// unset storage.
542 #[inline]
543 pub fn data_type(&self) -> DataType {
544 match self {
545 Self::Scalar(value) => value.data_type(),
546 Self::Collection(values) => values.data_type(),
547 }
548 }
549
550 /// Returns whether this container has scalar shape.
551 ///
552 /// # Returns
553 ///
554 /// `true` for [`ValueContainer::Scalar`].
555 #[inline]
556 #[must_use]
557 pub const fn is_scalar(&self) -> bool {
558 matches!(self, Self::Scalar(_))
559 }
560
561 /// Returns the contained scalar without consuming this container.
562 ///
563 /// # Returns
564 ///
565 /// `Some` for scalar storage, or `None` for collection storage.
566 #[must_use]
567 #[inline]
568 pub const fn as_scalar(&self) -> Option<&Value> {
569 match self {
570 Self::Scalar(value) => Some(value),
571 Self::Collection(_) => None,
572 }
573 }
574
575 /// Returns whether this container has collection shape.
576 ///
577 /// # Returns
578 ///
579 /// `true` for [`ValueContainer::Collection`].
580 #[inline]
581 #[must_use]
582 pub const fn is_collection(&self) -> bool {
583 matches!(self, Self::Collection(_))
584 }
585
586 /// Returns the contained collection without consuming this container.
587 ///
588 /// # Returns
589 ///
590 /// `Some` for collection storage, or `None` for scalar storage.
591 #[must_use]
592 #[inline]
593 pub const fn as_collection(&self) -> Option<&MultiValues> {
594 match self {
595 Self::Scalar(_) => None,
596 Self::Collection(values) => Some(values),
597 }
598 }
599
600 /// Returns whether the shape contains no concrete value or collection.
601 ///
602 /// # Returns
603 ///
604 /// `true` when the contained scalar or collection is unset.
605 #[inline(always)]
606 #[must_use]
607 pub fn is_unset(&self) -> bool {
608 match self {
609 Self::Scalar(value) => value.is_unset(),
610 Self::Collection(values) => values.is_unset(),
611 }
612 }
613
614 /// Returns zero for unset storage, one for a concrete scalar, or the
615 /// concrete collection length.
616 ///
617 /// # Returns
618 ///
619 /// The number of concrete values represented by this container.
620 #[inline]
621 #[must_use]
622 #[allow(clippy::len_without_is_empty)]
623 pub fn len(&self) -> usize {
624 match self {
625 Self::Scalar(value) => usize::from(!value.is_unset()),
626 Self::Collection(values) => values.len(),
627 }
628 }
629
630 /// Tests whether this container represents no concrete values.
631 ///
632 /// # Returns
633 ///
634 /// `true` for unset storage or a concrete empty collection.
635 #[inline]
636 #[must_use]
637 pub fn is_empty(&self) -> bool {
638 self.len() == 0
639 }
640
641 /// Strictly reads a scalar or the first collection item as `T`.
642 ///
643 /// # Type Parameters
644 ///
645 /// * `T` - Strict target type.
646 ///
647 /// # Returns
648 ///
649 /// The scalar value or first collection item.
650 ///
651 /// # Errors
652 ///
653 /// Returns [`ValueError::Missing`] for unset or empty matching storage and
654 /// [`ValueError::TypeMismatch`] when the stored data type differs.
655 #[inline(always)]
656 pub fn get_first<T>(&self) -> ValueResult<T>
657 where
658 T: StrictValueRead,
659 {
660 match self {
661 Self::Scalar(value) => T::read_scalar(value),
662 Self::Collection(values) => T::read_collection_first(values),
663 }
664 }
665
666 /// Strictly reads a scalar as a one-item list or all collection items.
667 ///
668 /// # Type Parameters
669 ///
670 /// * `T` - Strict target element type.
671 ///
672 /// # Returns
673 ///
674 /// A one-item scalar list or all collection items.
675 ///
676 /// # Errors
677 ///
678 /// Returns [`ValueError::Missing`] for unset matching storage and
679 /// [`ValueError::TypeMismatch`] when the stored data type differs.
680 #[inline(always)]
681 pub fn get_list<T>(&self) -> ValueResult<Vec<T>>
682 where
683 T: StrictValueRead,
684 {
685 match self {
686 Self::Scalar(value) => T::read_scalar(value).map(|item| vec![item]),
687 Self::Collection(values) => T::read_collection_list(values),
688 }
689 }
690
691 /// Converts a scalar or the first collection item to `T`.
692 ///
693 /// # Type Parameters
694 ///
695 /// * `T` - Target conversion type.
696 ///
697 /// # Returns
698 ///
699 /// The converted scalar or first collection item.
700 ///
701 /// # Errors
702 ///
703 /// Returns the mapped `qubit-datatype` conversion error.
704 #[cfg(feature = "converter")]
705 #[inline(always)]
706 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
707 pub fn to_first<T>(&self) -> ValueResult<T>
708 where
709 T: DataConversionTarget,
710 {
711 self.to_first_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
712 }
713
714 /// Converts a scalar or the first collection item using explicit policy and
715 /// limits.
716 ///
717 /// # Type Parameters
718 ///
719 /// * `T` - Target conversion type.
720 ///
721 /// # Parameters
722 ///
723 /// * `policy` - Conversion policy forwarded to the contained value.
724 /// * `limits` - Conversion limits forwarded to the contained value.
725 ///
726 /// # Returns
727 ///
728 /// The converted scalar or first collection item.
729 ///
730 /// # Errors
731 ///
732 /// Returns the mapped `qubit-datatype` conversion error.
733 #[cfg(feature = "converter")]
734 #[inline(always)]
735 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
736 pub fn to_first_with<T>(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<T>
737 where
738 T: DataConversionTarget,
739 {
740 match self {
741 Self::Scalar(value) => value.to_with(policy, limits),
742 Self::Collection(values) => values.to_first_with(policy, limits),
743 }
744 }
745
746 /// Converts the scalar or first collection item using an existing
747 /// conversion session.
748 ///
749 /// # Type Parameters
750 ///
751 /// * `T` - Target conversion type.
752 ///
753 /// # Parameters
754 ///
755 /// * `session` - Caller-owned session providing policy, limits, and budget.
756 ///
757 /// # Returns
758 ///
759 /// The converted scalar or first collection item.
760 ///
761 /// # Errors
762 ///
763 /// Returns the mapped missing, conversion, or budget error.
764 #[cfg(feature = "converter")]
765 #[inline(always)]
766 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
767 pub fn to_first_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<T>
768 where
769 T: DataConversionTarget,
770 {
771 match self {
772 Self::Scalar(value) => value.to_in(session),
773 Self::Collection(values) => values.to_first_in(session),
774 }
775 }
776
777 /// Converts a scalar to a list or converts every collection item.
778 ///
779 /// Scalar strings may be split according to collection conversion policy;
780 /// strings already stored in a collection are never split again.
781 ///
782 /// # Type Parameters
783 ///
784 /// * `T` - Target list element type.
785 ///
786 /// # Returns
787 ///
788 /// A converted scalar list or all converted collection items.
789 ///
790 /// # Errors
791 ///
792 /// Returns the mapped single-value or indexed list conversion error.
793 #[cfg(feature = "converter")]
794 #[inline(always)]
795 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
796 pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
797 where
798 T: DataConversionTarget,
799 {
800 self.to_list_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
801 }
802
803 /// Converts to a list using explicit conversion policy and limits.
804 ///
805 /// # Type Parameters
806 ///
807 /// * `T` - Target list element type.
808 ///
809 /// # Parameters
810 ///
811 /// * `policy` - Conversion policy forwarded to the contained value.
812 /// * `limits` - Conversion limits forwarded to the contained value.
813 ///
814 /// # Returns
815 ///
816 /// A converted scalar list or all converted collection items.
817 ///
818 /// # Errors
819 ///
820 /// Returns the mapped single-value or indexed list conversion error.
821 #[cfg(feature = "converter")]
822 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
823 pub fn to_list_with<T>(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<Vec<T>>
824 where
825 T: DataConversionTarget,
826 {
827 match self {
828 Self::Scalar(value) => match value.view() {
829 ValueRef::String(value) => ScalarStringDataConverters::from(value)
830 .to_vec_with(policy, limits)
831 .map_err(ValueError::from),
832 _ => value.to_with(policy, limits).map(|value| vec![value]),
833 },
834 Self::Collection(values) => values.to_list_with(policy, limits),
835 }
836 }
837
838 /// Converts this container to a list using an existing conversion session.
839 ///
840 /// # Type Parameters
841 ///
842 /// * `T` - Target list element type.
843 ///
844 /// # Parameters
845 ///
846 /// * `session` - Caller-owned session providing policy, limits, and budget.
847 ///
848 /// # Returns
849 ///
850 /// A converted scalar list or all converted collection items.
851 ///
852 /// # Errors
853 ///
854 /// Returns the mapped missing, conversion, indexed-list, or budget error.
855 #[cfg(feature = "converter")]
856 #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
857 pub fn to_list_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<Vec<T>>
858 where
859 T: DataConversionTarget,
860 {
861 match self {
862 Self::Scalar(value) => match value.view() {
863 ValueRef::String(value) => ScalarStringDataConverters::from(value)
864 .to_vec_in(session)
865 .map_err(ValueError::from),
866 _ => value.to_in(session).map(|value| vec![value]),
867 },
868 Self::Collection(values) => values.to_list_in(session),
869 }
870 }
871
872 /// Consumes this container and returns its scalar value.
873 ///
874 /// # Returns
875 ///
876 /// The contained scalar value when this container has scalar shape.
877 ///
878 /// # Errors
879 ///
880 /// Returns the original container unchanged when it has collection shape.
881 #[inline(always)]
882 pub fn into_scalar(self) -> Result<Value, Self> {
883 match self {
884 Self::Scalar(value) => Ok(value),
885 Self::Collection(_) => Err(self),
886 }
887 }
888
889 /// Consumes this container and returns its collection values.
890 ///
891 /// # Returns
892 ///
893 /// The contained values when this container has collection shape.
894 ///
895 /// # Errors
896 ///
897 /// Returns the original container unchanged when it has scalar shape.
898 #[inline(always)]
899 pub fn into_collection(self) -> Result<MultiValues, Self> {
900 match self {
901 Self::Scalar(_) => Err(self),
902 Self::Collection(values) => Ok(values),
903 }
904 }
905
906 /// Replaces this container, including its shape, from a supported input.
907 ///
908 /// # Type Parameters
909 ///
910 /// * `S` - Input type convertible into [`ValueContainer`].
911 ///
912 /// # Parameters
913 ///
914 /// * `value` - New scalar or collection value.
915 #[inline(always)]
916 pub fn set<S>(&mut self, value: S)
917 where
918 S: Into<Self>,
919 {
920 *self = value.into();
921 }
922
923 /// Appends values, promoting scalar storage to collection storage when the
924 /// input contains at least one concrete value. Same-typed empty and unset
925 /// input is a no-op.
926 ///
927 /// # Type Parameters
928 ///
929 /// * `S` - Input type convertible into [`ValueContainer`].
930 ///
931 /// # Parameters
932 ///
933 /// * `values` - Scalar or collection values to append.
934 ///
935 /// # Returns
936 ///
937 /// `Ok(())` after appending or accepting an empty same-typed input.
938 ///
939 /// # Errors
940 ///
941 /// Returns [`ValueError::TypeMismatch`] when the appended values have a
942 /// different data type.
943 pub fn add<S>(&mut self, values: S) -> ValueResult<()>
944 where
945 S: Into<Self>,
946 {
947 let other = values.into();
948 let expected = self.data_type();
949 let actual = other.data_type();
950 if expected != actual {
951 return Err(ValueError::TypeMismatch { expected, actual });
952 }
953 if other.is_empty() {
954 return Ok(());
955 }
956
957 match other {
958 Self::Scalar(value) => {
959 self.add_scalar(value, expected);
960 Ok(())
961 }
962 Self::Collection(other) => match self {
963 Self::Scalar(value) => {
964 let value = replace(value, Value::new_unset(expected));
965 let mut collection = MultiValues::from(value);
966 collection.add(other)?;
967 *self = Self::Collection(collection);
968 Ok(())
969 }
970 Self::Collection(collection) => collection.add(other),
971 },
972 }
973 }
974
975 /// Removes concrete storage while preserving its shape and data type.
976 #[inline(always)]
977 pub fn unset(&mut self) {
978 match self {
979 Self::Scalar(value) => value.unset(),
980 Self::Collection(values) => values.unset(),
981 }
982 }
983
984 /// Appends a concrete scalar, promoting scalar storage to a collection.
985 ///
986 /// # Parameters
987 ///
988 /// * `value` - Concrete scalar to append.
989 /// * `data_type` - Shared runtime type of `self` and `value`.
990 ///
991 /// # Constraints
992 ///
993 /// Callers must ensure `value` is concrete and has `data_type`; the public
994 /// mutation entry points validate those invariants before calling this
995 /// helper.
996 #[inline]
997 fn add_scalar(&mut self, value: Value, data_type: DataType) {
998 match self {
999 Self::Scalar(current) => {
1000 let current = replace(current, Value::new_unset(data_type));
1001 let collection = for_each_value_type!(value_container_pair_match, current, value);
1002 *self = Self::Collection(collection);
1003 }
1004 Self::Collection(collection) => {
1005 for_each_value_type!(value_container_push_match, collection, value);
1006 }
1007 }
1008 }
1009}
1010
1011#[cfg(all(feature = "converter", feature = "json"))]
1012#[cfg_attr(docsrs, doc(cfg(all(feature = "converter", feature = "json"))))]
1013impl ValueContainer {
1014 /// Projects this container while preserving concrete collection shape.
1015 ///
1016 /// Scalar storage uses the natural scalar projection; concrete collection
1017 /// storage always uses a JSON array.
1018 ///
1019 /// # Returns
1020 ///
1021 /// The natural JSON representation, except scalar and collection unset
1022 /// values both project to `null`.
1023 ///
1024 /// For natural JSON projection, `max_text_bytes` bounds each string value
1025 /// and each formatted number or rich-value text by its UTF-8 byte length,
1026 /// including strings inside materialized JSON. Object keys are excluded
1027 /// from this per-item bound but still consume applicable key and
1028 /// cumulative input, output, and payload budgets. This is neither a
1029 /// cumulative text limit nor a limit on escaped JSON encoding size.
1030 ///
1031 /// # Errors
1032 ///
1033 /// Returns the same structured projection error as the contained value.
1034 #[inline(always)]
1035 pub fn to_json_value(&self) -> ValueResult<JsonValue> {
1036 self.to_json_value_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
1037 }
1038
1039 /// Projects this container using explicit conversion policy and limits.
1040 ///
1041 /// # Parameters
1042 ///
1043 /// * `policy` - Controls duration units and precision-loss behavior.
1044 /// * `limits` - Bounds conversion resource consumption.
1045 ///
1046 /// # Returns
1047 ///
1048 /// The natural JSON representation, except scalar and collection unset
1049 /// values both project to `null`.
1050 ///
1051 /// # Errors
1052 ///
1053 /// Returns the same structured projection error as the contained value.
1054 #[inline]
1055 pub fn to_json_value_with(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<JsonValue> {
1056 crate::json::value_container_to_json_value_with(self, policy, limits)
1057 }
1058}