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 crate::multi_values::MultiValuesRepr;
12#[cfg(feature = "converter")]
13use crate::value::ValueRef;
14use crate::value::ValueRepr;
15use crate::{
16 MultiValues,
17 StrictValueRead,
18 Value,
19 ValueError,
20 ValueResult,
21};
22use qubit_datatype::DataType;
23#[cfg(feature = "converter")]
24use qubit_datatype::{
25 DataConversionOptions,
26 DataConversionTarget,
27 ScalarStringDataConverters,
28};
29
30/// A typed value whose scalar or collection shape is explicit.
31///
32/// The shape is never inferred from collection length. In particular,
33/// `Scalar(Value::Int32(42))` and
34/// `Collection(MultiValues::Int32(vec![42]))` remain distinguishable through
35/// conversion and serialization boundaries.
36#[must_use]
37#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38pub enum ValueContainer {
39 /// One typed value.
40 Scalar(
41 /// Stored scalar value.
42 Value,
43 ),
44 /// A homogeneous typed collection.
45 Collection(
46 /// Stored homogeneous collection.
47 MultiValues,
48 ),
49}
50
51macro_rules! impl_value_container_from_table {
52 (
53 ;
54 $(
55 (
56 [$($cfg:meta),*],
57 $variant:ident,
58 $type:ty,
59 $data_type:expr,
60 $materialization:ident,
61 $json_class:ident,
62 $number_projection:ident,
63 $value_doc:literal,
64 $multi_doc:literal
65 )
66 ),+ $(,)?
67 ) => {
68 $(
69 $(#[$cfg])*
70 impl From<$type> for ValueContainer {
71 #[inline(always)]
72 fn from(value: $type) -> Self {
73 Self::Scalar(Value::$variant(value))
74 }
75 }
76
77 $(#[$cfg])*
78 impl From<Vec<$type>> for ValueContainer {
79 #[inline(always)]
80 fn from(values: Vec<$type>) -> Self {
81 Self::Collection(MultiValues::$variant(values))
82 }
83 }
84
85 $(#[$cfg])*
86 impl From<&[$type]> for ValueContainer {
87 #[inline]
88 fn from(values: &[$type]) -> Self {
89 Self::Collection(MultiValues::$variant(values.to_vec()))
90 }
91 }
92
93 $(#[$cfg])*
94 impl From<&Vec<$type>> for ValueContainer {
95 #[inline]
96 fn from(values: &Vec<$type>) -> Self {
97 Self::Collection(MultiValues::$variant(values.clone()))
98 }
99 }
100
101 $(#[$cfg])*
102 impl<const N: usize> From<[$type; N]> for ValueContainer {
103 #[inline]
104 fn from(values: [$type; N]) -> Self {
105 Self::Collection(MultiValues::$variant(Vec::from(values)))
106 }
107 }
108
109 $(#[$cfg])*
110 impl<const N: usize> From<&[$type; N]> for ValueContainer {
111 #[inline]
112 fn from(values: &[$type; N]) -> Self {
113 Self::Collection(MultiValues::$variant(values.to_vec()))
114 }
115 }
116 )+
117 };
118}
119
120for_each_value_type!(impl_value_container_from_table);
121
122/// Builds a typed collection from one or two same-typed scalar values.
123macro_rules! value_container_pair_match {
124 ($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)),+ $(,)?) => {
125 match ($first.repr, $second.repr) {
126 $(
127 $(#[$cfg])*
128 (ValueRepr::$variant(first), ValueRepr::$variant(second)) => {
129 MultiValues::$variant(vec![
130 value_storage_into_multi!($variant, first),
131 value_storage_into_multi!($variant, second),
132 ])
133 }
134 )+
135 $(
136 $(#[$cfg])*
137 (ValueRepr::Unset(_), ValueRepr::$variant(second)) => {
138 MultiValues::$variant(vec![value_storage_into_multi!($variant, second)])
139 }
140 )+
141 _ => unreachable!(),
142 }
143 };
144}
145
146/// Pushes a same-typed scalar directly into collection storage.
147macro_rules! value_container_push_match {
148 ($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)),+ $(,)?) => {
149 match (&mut $collection.repr, $value.repr) {
150 $(
151 $(#[$cfg])*
152 (MultiValuesRepr::$variant(values), ValueRepr::$variant(value)) => {
153 values.push(value_storage_into_multi!($variant, value))
154 },
155 )+
156 $(
157 $(#[$cfg])*
158 (slot @ MultiValuesRepr::Unset(_), ValueRepr::$variant(value)) => {
159 *slot = MultiValuesRepr::$variant(vec![value_storage_into_multi!($variant, value)]);
160 }
161 )+
162 _ => unreachable!(),
163 }
164 };
165}
166
167impl From<&str> for ValueContainer {
168 #[inline]
169 fn from(value: &str) -> Self {
170 Self::Scalar(Value::String(value.to_string()))
171 }
172}
173
174impl<'a> From<Vec<&'a str>> for ValueContainer {
175 #[inline]
176 fn from(values: Vec<&'a str>) -> Self {
177 Self::Collection(MultiValues::from(values))
178 }
179}
180
181impl<'a, 'b> From<&'a [&'b str]> for ValueContainer {
182 #[inline]
183 fn from(values: &'a [&'b str]) -> Self {
184 Self::Collection(MultiValues::from(values))
185 }
186}
187
188impl<'a, 'b> From<&'a Vec<&'b str>> for ValueContainer {
189 #[inline]
190 fn from(values: &'a Vec<&'b str>) -> Self {
191 Self::Collection(MultiValues::from(values))
192 }
193}
194
195impl<'a, const N: usize> From<[&'a str; N]> for ValueContainer {
196 #[inline]
197 fn from(values: [&'a str; N]) -> Self {
198 Self::Collection(MultiValues::from(values))
199 }
200}
201
202impl<'a, 'b, const N: usize> From<&'a [&'b str; N]> for ValueContainer {
203 #[inline]
204 fn from(values: &'a [&'b str; N]) -> Self {
205 Self::Collection(MultiValues::from(values))
206 }
207}
208
209impl From<Value> for ValueContainer {
210 #[inline(always)]
211 fn from(value: Value) -> Self {
212 Self::Scalar(value)
213 }
214}
215
216impl From<MultiValues> for ValueContainer {
217 #[inline(always)]
218 fn from(values: MultiValues) -> Self {
219 Self::Collection(values)
220 }
221}
222
223impl ValueContainer {
224 /// Returns the stored or declared data type.
225 ///
226 /// # Returns
227 ///
228 /// The scalar or collection element type, including the declared type of
229 /// unset storage.
230 #[inline(always)]
231 pub fn data_type(&self) -> DataType {
232 match self {
233 Self::Scalar(value) => value.data_type(),
234 Self::Collection(values) => values.data_type(),
235 }
236 }
237
238 /// Creates an unset scalar container for a declared data type.
239 ///
240 /// # Parameters
241 ///
242 /// * `data_type` - Declared scalar type while no value is set.
243 ///
244 /// # Returns
245 ///
246 /// A scalar `ValueContainer` with explicit unset storage.
247 #[inline(always)]
248 pub const fn new_unset_scalar(data_type: DataType) -> Self {
249 Self::Scalar(Value::new_unset(data_type))
250 }
251
252 /// Creates an unset collection container for a declared element type.
253 ///
254 /// # Parameters
255 ///
256 /// * `data_type` - Declared element type while no values are set.
257 ///
258 /// # Returns
259 ///
260 /// A collection `ValueContainer` with explicit unset storage.
261 #[inline(always)]
262 pub const fn new_unset_collection(data_type: DataType) -> Self {
263 Self::Collection(MultiValues::new_unset(data_type))
264 }
265
266 /// Returns whether this container has scalar shape.
267 ///
268 /// # Returns
269 ///
270 /// `true` for [`ValueContainer::Scalar`].
271 #[inline(always)]
272 #[must_use]
273 pub const fn is_scalar(&self) -> bool {
274 matches!(self, Self::Scalar(_))
275 }
276
277 /// Returns the contained scalar without consuming this container.
278 ///
279 /// # Returns
280 ///
281 /// `Some` for scalar storage, or `None` for collection storage.
282 #[inline(always)]
283 pub const fn as_scalar(&self) -> Option<&Value> {
284 match self {
285 Self::Scalar(value) => Some(value),
286 Self::Collection(_) => None,
287 }
288 }
289
290 /// Consumes this container and returns its scalar value.
291 ///
292 /// # Returns
293 ///
294 /// The contained scalar value when this container has scalar shape.
295 ///
296 /// # Errors
297 ///
298 /// Returns the original container unchanged when it has collection shape.
299 #[inline(always)]
300 pub fn into_scalar(self) -> Result<Value, Self> {
301 match self {
302 Self::Scalar(value) => Ok(value),
303 Self::Collection(_) => Err(self),
304 }
305 }
306
307 /// Returns whether this container has collection shape.
308 ///
309 /// # Returns
310 ///
311 /// `true` for [`ValueContainer::Collection`].
312 #[inline(always)]
313 #[must_use]
314 pub const fn is_collection(&self) -> bool {
315 matches!(self, Self::Collection(_))
316 }
317
318 /// Returns the contained collection without consuming this container.
319 ///
320 /// # Returns
321 ///
322 /// `Some` for collection storage, or `None` for scalar storage.
323 #[inline(always)]
324 pub const fn as_collection(&self) -> Option<&MultiValues> {
325 match self {
326 Self::Scalar(_) => None,
327 Self::Collection(values) => Some(values),
328 }
329 }
330
331 /// Consumes this container and returns its collection values.
332 ///
333 /// # Returns
334 ///
335 /// The contained values when this container has collection shape.
336 ///
337 /// # Errors
338 ///
339 /// Returns the original container unchanged when it has scalar shape.
340 #[inline(always)]
341 pub fn into_collection(self) -> Result<MultiValues, Self> {
342 match self {
343 Self::Scalar(_) => Err(self),
344 Self::Collection(values) => Ok(values),
345 }
346 }
347
348 /// Returns whether the shape contains no concrete value or collection.
349 ///
350 /// # Returns
351 ///
352 /// `true` when the contained scalar or collection is unset.
353 #[inline(always)]
354 #[must_use]
355 pub fn is_unset(&self) -> bool {
356 match self {
357 Self::Scalar(value) => value.is_unset(),
358 Self::Collection(values) => values.is_unset(),
359 }
360 }
361
362 /// Returns zero for unset storage, one for a concrete scalar, or the
363 /// concrete collection length.
364 ///
365 /// # Returns
366 ///
367 /// The number of concrete values represented by this container.
368 #[inline(always)]
369 #[must_use]
370 #[allow(clippy::len_without_is_empty)]
371 pub fn len(&self) -> usize {
372 match self {
373 Self::Scalar(value) => usize::from(!value.is_unset()),
374 Self::Collection(values) => values.len(),
375 }
376 }
377
378 /// Tests whether this container represents no concrete values.
379 #[inline(always)]
380 #[must_use]
381 pub fn is_empty(&self) -> bool {
382 self.len() == 0
383 }
384
385 /// Strictly reads a scalar or the first collection item as `T`.
386 ///
387 /// # Type Parameters
388 ///
389 /// * `T` - Strict target type.
390 ///
391 /// # Returns
392 ///
393 /// The scalar value or first collection item.
394 ///
395 /// # Errors
396 ///
397 /// Returns [`ValueError::Missing`] for unset or empty matching storage and
398 /// [`ValueError::TypeMismatch`] when the stored data type differs.
399 #[inline(always)]
400 pub fn get_first<T>(&self) -> ValueResult<T>
401 where
402 T: StrictValueRead,
403 {
404 match self {
405 Self::Scalar(value) => T::read_scalar(value),
406 Self::Collection(values) => T::read_collection_first(values),
407 }
408 }
409
410 /// Strictly reads a scalar as a one-item list or all collection items.
411 ///
412 /// # Type Parameters
413 ///
414 /// * `T` - Strict target element type.
415 ///
416 /// # Returns
417 ///
418 /// A one-item scalar list or all collection items.
419 ///
420 /// # Errors
421 ///
422 /// Returns [`ValueError::Missing`] for unset matching storage and
423 /// [`ValueError::TypeMismatch`] when the stored data type differs.
424 #[inline(always)]
425 pub fn get_list<T>(&self) -> ValueResult<Vec<T>>
426 where
427 T: StrictValueRead,
428 {
429 match self {
430 Self::Scalar(value) => T::read_scalar(value).map(|item| vec![item]),
431 Self::Collection(values) => T::read_collection_list(values),
432 }
433 }
434
435 /// Replaces this container, including its shape, from a supported input.
436 ///
437 /// # Type Parameters
438 ///
439 /// * `S` - Input type convertible into [`ValueContainer`].
440 ///
441 /// # Parameters
442 ///
443 /// * `value` - New scalar or collection value.
444 #[inline(always)]
445 pub fn set<S>(&mut self, value: S)
446 where
447 S: Into<Self>,
448 {
449 *self = value.into();
450 }
451
452 /// Appends values, promoting scalar storage to collection storage when the
453 /// input contains at least one concrete value. Same-typed empty and unset
454 /// input is a no-op.
455 ///
456 /// # Type Parameters
457 ///
458 /// * `S` - Input type convertible into [`ValueContainer`].
459 ///
460 /// # Parameters
461 ///
462 /// * `values` - Scalar or collection values to append.
463 ///
464 /// # Returns
465 ///
466 /// `Ok(())` after appending or accepting an empty same-typed input.
467 ///
468 /// # Errors
469 ///
470 /// Returns [`ValueError::TypeMismatch`] when the appended values have a
471 /// different data type.
472 pub fn add<S>(&mut self, values: S) -> ValueResult<()>
473 where
474 S: Into<Self>,
475 {
476 let other = values.into();
477 let expected = self.data_type();
478 let actual = other.data_type();
479 if expected != actual {
480 return Err(ValueError::TypeMismatch { expected, actual });
481 }
482 if other.is_empty() {
483 return Ok(());
484 }
485
486 match other {
487 Self::Scalar(value) => {
488 self.add_scalar(value, expected);
489 Ok(())
490 }
491 Self::Collection(other) => match self {
492 Self::Scalar(value) => {
493 let value =
494 std::mem::replace(value, Value::new_unset(expected));
495 let mut collection = MultiValues::from(value);
496 collection.add(other)?;
497 *self = Self::Collection(collection);
498 Ok(())
499 }
500 Self::Collection(collection) => collection.add(other),
501 },
502 }
503 }
504
505 /// Removes concrete storage while preserving its shape and data type.
506 #[inline(always)]
507 pub fn unset(&mut self) {
508 match self {
509 Self::Scalar(value) => value.unset(),
510 Self::Collection(values) => values.unset(),
511 }
512 }
513
514 /// Converts a scalar or the first collection item to `T`.
515 ///
516 /// # Type Parameters
517 ///
518 /// * `T` - Target conversion type.
519 ///
520 /// # Returns
521 ///
522 /// The converted scalar or first collection item.
523 ///
524 /// # Errors
525 ///
526 /// Returns the mapped `qubit-datatype` conversion error.
527 #[cfg(feature = "converter")]
528 #[inline(always)]
529 pub fn to_first<T>(&self) -> ValueResult<T>
530 where
531 T: DataConversionTarget,
532 {
533 self.to_first_with(DataConversionOptions::default_ref())
534 }
535
536 /// Converts a scalar or the first collection item using explicit options.
537 ///
538 /// # Type Parameters
539 ///
540 /// * `T` - Target conversion type.
541 ///
542 /// # Parameters
543 ///
544 /// * `options` - Conversion options forwarded to the contained value.
545 ///
546 /// # Returns
547 ///
548 /// The converted scalar or first collection item.
549 ///
550 /// # Errors
551 ///
552 /// Returns the mapped `qubit-datatype` conversion error.
553 #[cfg(feature = "converter")]
554 #[inline(always)]
555 pub fn to_first_with<T>(
556 &self,
557 options: &DataConversionOptions,
558 ) -> ValueResult<T>
559 where
560 T: DataConversionTarget,
561 {
562 match self {
563 Self::Scalar(value) => value.to_with(options),
564 Self::Collection(values) => values.to_first_with(options),
565 }
566 }
567
568 /// Converts a scalar to a list or converts every collection item.
569 ///
570 /// Scalar strings may be split according to collection conversion options;
571 /// strings already stored in a collection are never split again.
572 ///
573 /// # Type Parameters
574 ///
575 /// * `T` - Target list element type.
576 ///
577 /// # Returns
578 ///
579 /// A converted scalar list or all converted collection items.
580 ///
581 /// # Errors
582 ///
583 /// Returns the mapped single-value or indexed list conversion error.
584 #[cfg(feature = "converter")]
585 #[inline(always)]
586 pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
587 where
588 T: DataConversionTarget,
589 {
590 self.to_list_with(DataConversionOptions::default_ref())
591 }
592
593 /// Converts to a list using explicit conversion options.
594 ///
595 /// # Type Parameters
596 ///
597 /// * `T` - Target list element type.
598 ///
599 /// # Parameters
600 ///
601 /// * `options` - Conversion options forwarded to the contained value.
602 ///
603 /// # Returns
604 ///
605 /// A converted scalar list or all converted collection items.
606 ///
607 /// # Errors
608 ///
609 /// Returns the mapped single-value or indexed list conversion error.
610 #[cfg(feature = "converter")]
611 pub fn to_list_with<T>(
612 &self,
613 options: &DataConversionOptions,
614 ) -> ValueResult<Vec<T>>
615 where
616 T: DataConversionTarget,
617 {
618 match self {
619 Self::Scalar(value) => match value.view() {
620 ValueRef::String(value) => {
621 ScalarStringDataConverters::from(value)
622 .to_vec_with(options)
623 .map_err(ValueError::from)
624 }
625 _ => value.to_with(options).map(|value| vec![value]),
626 },
627 Self::Collection(values) => values.to_list_with(options),
628 }
629 }
630
631 /// Appends a concrete scalar, promoting scalar storage to a collection.
632 ///
633 /// # Parameters
634 ///
635 /// * `value` - Concrete scalar to append.
636 /// * `data_type` - Shared runtime type of `self` and `value`.
637 ///
638 /// # Constraints
639 ///
640 /// Callers must ensure `value` is concrete and has `data_type`; the public
641 /// mutation entry points validate those invariants before calling this
642 /// helper.
643 #[inline]
644 fn add_scalar(&mut self, value: Value, data_type: DataType) {
645 match self {
646 Self::Scalar(current) => {
647 let current =
648 std::mem::replace(current, Value::new_unset(data_type));
649 let collection = for_each_value_type!(
650 value_container_pair_match,
651 current,
652 value
653 );
654 *self = Self::Collection(collection);
655 }
656 Self::Collection(collection) => {
657 for_each_value_type!(
658 value_container_push_match,
659 collection,
660 value
661 );
662 }
663 }
664 }
665}