qubit_value/multi_values/multi_values_converters.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//! Internal conversion and interoperability implementations for `MultiValues`.
10//!
11//! This module keeps generic conversion logic (`to_first` and `to_list`).
12
13use qubit_datatype::{
14 DataConversionError,
15 DataConversionOptions,
16 DataConversionTarget,
17 DataConverter,
18 DataConverters,
19};
20
21use crate::IntoValueDefault;
22use crate::value_error::{
23 ValueError,
24 ValueResult,
25};
26
27use super::multi_values::{
28 MultiValues,
29 MultiValuesRepr,
30};
31
32macro_rules! multi_values_convert_first_match {
33 ($value:expr, $options:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal)),+ $(,)?) => {
34 match &$value.repr {
35 MultiValuesRepr::Unset(from) => {
36 Err(DataConversionError::missing(*from, T::DATA_TYPE).into())
37 }
38 $(
39 $(#[$cfg])*
40 MultiValuesRepr::$variant(values) => {
41 convert_first_with(DataConverters::from(values), $options)
42 }
43 )+
44 }
45 };
46}
47
48macro_rules! multi_values_convert_list_match {
49 ($value:expr, $options:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal)),+ $(,)?) => {
50 match &$value.repr {
51 MultiValuesRepr::Unset(from) => {
52 Err(DataConversionError::missing(*from, T::DATA_TYPE).into())
53 }
54 $(
55 $(#[$cfg])*
56 MultiValuesRepr::$variant(values) => {
57 convert_values_with(DataConverters::from(values), $options)
58 }
59 )+
60 }
61 };
62}
63
64// ============================================================================
65// Inherent conversion APIs
66// ============================================================================
67
68/// Converts the first item from a batch converter using conversion options.
69///
70/// # Type Parameters
71///
72/// * `T` - Target type.
73/// * `I` - Iterator type wrapped by `DataConverters`.
74///
75/// # Parameters
76///
77/// * `values` - Batch converter containing source values.
78/// * `options` - Conversion options forwarded to `qubit_datatype`.
79///
80/// # Returns
81///
82/// Returns the converted first value.
83///
84/// # Errors
85///
86/// Returns the mapped single-value conversion error for an empty source or an
87/// invalid first source value.
88#[inline(always)]
89fn convert_first_with<'a, T, I>(
90 values: DataConverters<I>,
91 options: &DataConversionOptions,
92) -> ValueResult<T>
93where
94 T: DataConversionTarget,
95 I: Iterator,
96 I::Item: Into<DataConverter<'a>>,
97{
98 values.to_first_with(options).map_err(ValueError::from)
99}
100
101/// Converts every item from a batch converter using conversion options.
102///
103/// # Type Parameters
104///
105/// * `T` - Target element type.
106/// * `I` - Iterator type wrapped by `DataConverters`.
107///
108/// # Parameters
109///
110/// * `values` - Batch converter containing source values.
111/// * `options` - Conversion options forwarded to `qubit_datatype`.
112///
113/// # Returns
114///
115/// Returns converted values in the original order.
116///
117/// # Errors
118///
119/// Returns a mapped batch conversion error containing the failing source index.
120#[inline(always)]
121fn convert_values_with<'a, T, I>(
122 values: DataConverters<I>,
123 options: &DataConversionOptions,
124) -> ValueResult<Vec<T>>
125where
126 T: DataConversionTarget,
127 I: Iterator,
128 I::Item: Into<DataConverter<'a>>,
129{
130 values.to_vec_with(options).map_err(ValueError::from)
131}
132
133impl MultiValues {
134 /// Converts the first stored value to `T`.
135 ///
136 /// Unlike [`Self::get_first`], this method uses shared `DataConverter`
137 /// conversion rules instead of strict type matching. For example, a stored
138 /// `String("1")` can be converted to `bool`.
139 ///
140 /// # Type Parameters
141 ///
142 /// * `T` - Target type.
143 ///
144 /// # Returns
145 ///
146 /// The converted first value.
147 ///
148 /// # Errors
149 ///
150 /// Returns a structured missing-value conversion error when the container
151 /// is unset, an empty-collection error for a concrete empty vector, or a
152 /// conversion error when the first value cannot be converted to `T`.
153 #[inline(always)]
154 pub fn to_first<T>(&self) -> ValueResult<T>
155 where
156 T: DataConversionTarget,
157 {
158 self.to_first_with(DataConversionOptions::default_ref())
159 }
160
161 /// Converts the first stored value to `T`, or returns `default` when the
162 /// container is unset or conversion reports a missing value.
163 ///
164 /// A concrete empty collection remains an error and does not use the
165 /// default.
166 ///
167 /// # Type Parameters
168 ///
169 /// * `T` - Target type.
170 ///
171 /// # Parameters
172 ///
173 /// * `default` - Value returned for unset storage or a conversion-missing
174 /// result.
175 ///
176 /// # Returns
177 ///
178 /// The converted first value, or `default` for unset or conversion-missing
179 /// storage.
180 ///
181 /// # Errors
182 ///
183 /// Returns an empty-collection error for a concrete empty vector, or a
184 /// conversion error when the first value cannot be converted to `T`.
185 #[inline]
186 pub fn to_first_or<T>(
187 &self,
188 default: impl IntoValueDefault<T>,
189 ) -> ValueResult<T>
190 where
191 T: DataConversionTarget,
192 {
193 match self.to_first() {
194 Err(ValueError::Missing(missing))
195 if missing.is_defaultable_for_conversion() =>
196 {
197 Ok(default.into_value_default())
198 }
199 result => result,
200 }
201 }
202
203 /// Converts the first value or calls `default` when storage is unset or
204 /// conversion reports a missing value.
205 ///
206 /// # Type Parameters
207 ///
208 /// * `T` - Target conversion type.
209 /// * `F` - Deferred fallback producing `T`.
210 ///
211 /// # Parameters
212 ///
213 /// * `default` - Callback invoked for unset storage or a conversion-missing
214 /// result.
215 ///
216 /// # Returns
217 ///
218 /// The converted first item or the callback result.
219 ///
220 /// # Errors
221 ///
222 /// Preserves empty-collection and concrete-value conversion errors without
223 /// invoking the callback.
224 #[inline]
225 pub fn to_first_or_else<T, F>(&self, default: F) -> ValueResult<T>
226 where
227 T: DataConversionTarget,
228 F: FnOnce() -> T,
229 {
230 match self.to_first() {
231 Err(ValueError::Missing(missing))
232 if missing.is_defaultable_for_conversion() =>
233 {
234 Ok(default())
235 }
236 result => result,
237 }
238 }
239
240 /// Converts the first stored value to `T` using conversion options.
241 ///
242 /// Stored strings are collection items and are never split again by scalar
243 /// string collection options.
244 ///
245 /// # Type Parameters
246 ///
247 /// * `T` - Target type.
248 ///
249 /// # Parameters
250 ///
251 /// * `options` - Conversion options forwarded to `qubit_datatype`.
252 ///
253 /// # Returns
254 ///
255 /// The converted first value.
256 ///
257 /// # Errors
258 ///
259 /// Returns a structured missing-value conversion error when the container
260 /// is unset, an empty-collection error for a concrete empty vector, or a
261 /// conversion error when the first value cannot be converted to `T`.
262 pub fn to_first_with<T>(
263 &self,
264 options: &DataConversionOptions,
265 ) -> ValueResult<T>
266 where
267 T: DataConversionTarget,
268 {
269 for_each_value_type!(multi_values_convert_first_match, self, options)
270 }
271
272 /// Converts the first stored value to `T` using conversion options, or
273 /// returns `default` when storage is unset or conversion reports a missing
274 /// value.
275 ///
276 /// # Type Parameters
277 ///
278 /// * `T` - Target conversion type.
279 ///
280 /// # Parameters
281 ///
282 /// * `default` - Lazily materialized value used for unset storage or a
283 /// conversion-missing result.
284 /// * `options` - Conversion options forwarded to `qubit_datatype`.
285 ///
286 /// # Returns
287 ///
288 /// The converted first item, or `default` for unset or conversion-missing
289 /// storage.
290 ///
291 /// # Errors
292 ///
293 /// Returns an empty-collection error or a conversion error for concrete
294 /// values that cannot be converted under `options`.
295 #[inline]
296 pub fn to_first_or_with<T>(
297 &self,
298 default: impl IntoValueDefault<T>,
299 options: &DataConversionOptions,
300 ) -> ValueResult<T>
301 where
302 T: DataConversionTarget,
303 {
304 match self.to_first_with(options) {
305 Err(ValueError::Missing(missing))
306 if missing.is_defaultable_for_conversion() =>
307 {
308 Ok(default.into_value_default())
309 }
310 result => result,
311 }
312 }
313
314 /// Converts the first value with `options`, or calls `default` when storage
315 /// is unset or conversion reports a missing value.
316 ///
317 /// # Type Parameters
318 ///
319 /// * `T` - Target conversion type.
320 /// * `F` - Deferred fallback producing `T`.
321 ///
322 /// # Parameters
323 ///
324 /// * `default` - Callback invoked for unset storage or a conversion-missing
325 /// result.
326 /// * `options` - Conversion options forwarded to the shared converter.
327 ///
328 /// # Returns
329 ///
330 /// The converted first item or the callback result.
331 ///
332 /// # Errors
333 ///
334 /// Preserves concrete-value conversion errors without invoking the
335 /// callback.
336 #[inline]
337 pub fn to_first_or_else_with<T, F>(
338 &self,
339 default: F,
340 options: &DataConversionOptions,
341 ) -> ValueResult<T>
342 where
343 T: DataConversionTarget,
344 F: FnOnce() -> T,
345 {
346 match self.to_first_with(options) {
347 Err(ValueError::Missing(missing))
348 if missing.is_defaultable_for_conversion() =>
349 {
350 Ok(default())
351 }
352 result => result,
353 }
354 }
355
356 /// Converts all stored values to `T`.
357 ///
358 /// Unlike [`Self::get`], this method uses shared `DataConverter` conversion
359 /// rules for every element instead of strict type matching. A concrete
360 /// empty vector returns an empty vector; an unset container reports a
361 /// missing-value conversion error.
362 ///
363 /// # Type Parameters
364 ///
365 /// * `T` - Target element type.
366 ///
367 /// # Returns
368 ///
369 /// A vector containing all converted values in the original order.
370 ///
371 /// # Errors
372 ///
373 /// Returns the first conversion error encountered while converting an
374 /// element.
375 pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
376 where
377 T: DataConversionTarget,
378 {
379 self.to_list_with(DataConversionOptions::default_ref())
380 }
381
382 /// Converts all stored values to `T`, or returns `default` when storage is
383 /// unset or conversion reports a missing value.
384 ///
385 /// # Type Parameters
386 ///
387 /// * `T` - Target element type.
388 ///
389 /// # Parameters
390 ///
391 /// * `default` - Lazily materialized list used for unset storage or a
392 /// conversion-missing result.
393 ///
394 /// # Returns
395 ///
396 /// All converted items, or `default` for unset or conversion-missing
397 /// storage.
398 ///
399 /// # Errors
400 ///
401 /// Returns the first item conversion error for concrete storage.
402 #[inline]
403 pub fn to_list_or<T>(
404 &self,
405 default: impl IntoValueDefault<Vec<T>>,
406 ) -> ValueResult<Vec<T>>
407 where
408 T: DataConversionTarget,
409 {
410 match self.to_list() {
411 Err(ValueError::Missing(missing))
412 if missing.is_defaultable_for_conversion() =>
413 {
414 Ok(default.into_value_default())
415 }
416 result => result,
417 }
418 }
419
420 /// Converts all values or calls `default` when storage is unset or
421 /// conversion reports a missing value.
422 ///
423 /// # Type Parameters
424 ///
425 /// * `T` - Target element conversion type.
426 /// * `F` - Deferred fallback producing the complete list.
427 ///
428 /// # Parameters
429 ///
430 /// * `default` - Callback invoked for unset storage or a conversion-missing
431 /// result.
432 ///
433 /// # Returns
434 ///
435 /// The converted list or the callback result.
436 ///
437 /// # Errors
438 ///
439 /// Preserves concrete-value conversion errors without invoking the
440 /// callback.
441 #[inline]
442 pub fn to_list_or_else<T, F>(&self, default: F) -> ValueResult<Vec<T>>
443 where
444 T: DataConversionTarget,
445 F: FnOnce() -> Vec<T>,
446 {
447 match self.to_list() {
448 Err(ValueError::Missing(missing))
449 if missing.is_defaultable_for_conversion() =>
450 {
451 Ok(default())
452 }
453 result => result,
454 }
455 }
456
457 /// Converts all stored values to `T` using conversion options.
458 ///
459 /// Stored strings are collection items and are never split again by scalar
460 /// string collection options.
461 ///
462 /// # Type Parameters
463 ///
464 /// * `T` - Target element type.
465 ///
466 /// # Parameters
467 ///
468 /// * `options` - Conversion options forwarded to `qubit_datatype`.
469 ///
470 /// # Returns
471 ///
472 /// A vector containing all converted values in the original order.
473 ///
474 /// # Errors
475 ///
476 /// Returns the first conversion error encountered while converting an
477 /// element.
478 pub fn to_list_with<T>(
479 &self,
480 options: &DataConversionOptions,
481 ) -> ValueResult<Vec<T>>
482 where
483 T: DataConversionTarget,
484 {
485 for_each_value_type!(multi_values_convert_list_match, self, options)
486 }
487
488 /// Converts all stored values to `T` using conversion options, or returns
489 /// `default` when storage is unset or conversion reports a missing value.
490 ///
491 /// # Type Parameters
492 ///
493 /// * `T` - Target element type.
494 ///
495 /// # Parameters
496 ///
497 /// * `default` - Lazily materialized list used for unset storage or a
498 /// conversion-missing result.
499 /// * `options` - Conversion options forwarded to `qubit_datatype`.
500 ///
501 /// # Returns
502 ///
503 /// All converted items, or `default` for unset or conversion-missing
504 /// storage.
505 ///
506 /// # Errors
507 ///
508 /// Returns the first item conversion error for concrete storage.
509 #[inline]
510 pub fn to_list_or_with<T>(
511 &self,
512 default: impl IntoValueDefault<Vec<T>>,
513 options: &DataConversionOptions,
514 ) -> ValueResult<Vec<T>>
515 where
516 T: DataConversionTarget,
517 {
518 match self.to_list_with(options) {
519 Err(ValueError::Missing(missing))
520 if missing.is_defaultable_for_conversion() =>
521 {
522 Ok(default.into_value_default())
523 }
524 result => result,
525 }
526 }
527
528 /// Converts all values with `options`, or calls `default` when storage is
529 /// unset or conversion reports a missing value.
530 ///
531 /// # Type Parameters
532 ///
533 /// * `T` - Target element conversion type.
534 /// * `F` - Deferred fallback producing the complete list.
535 ///
536 /// # Parameters
537 ///
538 /// * `default` - Callback invoked for unset storage or a conversion-missing
539 /// result.
540 /// * `options` - Conversion options forwarded to the shared converter.
541 ///
542 /// # Returns
543 ///
544 /// The converted list or the callback result.
545 ///
546 /// # Errors
547 ///
548 /// Preserves concrete-value conversion errors without invoking the
549 /// callback.
550 #[inline]
551 pub fn to_list_or_else_with<T, F>(
552 &self,
553 default: F,
554 options: &DataConversionOptions,
555 ) -> ValueResult<Vec<T>>
556 where
557 T: DataConversionTarget,
558 F: FnOnce() -> Vec<T>,
559 {
560 match self.to_list_with(options) {
561 Err(ValueError::Missing(missing))
562 if missing.is_defaultable_for_conversion() =>
563 {
564 Ok(default())
565 }
566 result => result,
567 }
568 }
569}