qubit_value/value/internal/value_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 implementations for value conversion support.
10//!
11//! This module focuses on conversion helpers backed by `qubit_datatype`.
12
13use qubit_datatype::ConversionLimits;
14use qubit_datatype::ConversionPolicy;
15use qubit_datatype::ConversionSession;
16use qubit_datatype::DataConversionError;
17use qubit_datatype::DataConversionTarget;
18use qubit_datatype::DataConverter;
19
20use crate::ValueMissingReason;
21use crate::ValueRef;
22use crate::value::Value;
23use crate::value::ValueRepr;
24use crate::value_error::ValueError;
25use crate::value_error::ValueResult;
26
27/// Converts semantic views without constructing an owned runtime value.
28macro_rules! value_ref_data_converter_match {
29 ($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)*)),+ $(,)?) => {
30 match $value {
31 ValueRef::Unset(data_type) => DataConverter::Unset(data_type),
32 $($(#[$cfg])* ValueRef::$variant(value) => DataConverter::from(value),)+
33 }
34 };
35}
36
37impl<'a> From<ValueRef<'a>> for DataConverter<'a> {
38 /// Borrows rich view payloads and copies primitive conversion sources.
39 ///
40 /// # Parameters
41 ///
42 /// * `value` - Semantic view whose rich payloads remain borrowed for `'a`.
43 ///
44 /// # Returns
45 ///
46 /// A converter preserving the view's data type and borrowing rich payloads
47 /// without cloning them.
48 #[inline]
49 fn from(value: ValueRef<'a>) -> Self {
50 for_each_value_type!(value_ref_data_converter_match, value)
51 }
52}
53
54/// Expands the shared value table into a `DataConverter` construction match.
55macro_rules! value_data_converter_match {
56 ($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)*)),+ $(,)?) => {
57 match &$value.repr {
58 ValueRepr::Unset(data_type) => DataConverter::Unset(*data_type),
59 $($(#[$cfg])* ValueRepr::$variant(value) => DataConverter::from(value_storage_ref!($variant, value)),)+
60 }
61 };
62}
63
64/// Wraps a `Value` into the common conversion helper for the `qubit_datatype`
65/// conversion API.
66///
67/// # Parameters
68///
69/// * `value` - Runtime value whose payload is borrowed by the converter.
70///
71/// # Returns
72///
73/// A shared converter view preserving the runtime data type.
74#[inline]
75fn data_converter_from_value(value: &Value) -> DataConverter<'_> {
76 for_each_value_type!(value_data_converter_match, value)
77}
78
79impl<'a> From<&'a Value> for DataConverter<'a> {
80 /// Borrows a runtime value as a shared conversion source.
81 ///
82 /// # Parameters
83 ///
84 /// * `value` - Runtime value whose storage is exposed to the converter.
85 ///
86 /// # Returns
87 ///
88 /// A [`DataConverter`] borrowing rich payloads from `value` without
89 /// cloning them.
90 #[inline(always)]
91 fn from(value: &'a Value) -> Self {
92 data_converter_from_value(value)
93 }
94}
95
96/// Converts a single `Value` into `T` using shared conversion helpers,
97/// conversion policy, and resource limits.
98///
99/// # Type Parameters
100///
101/// * `T` - Target type supported by the shared conversion layer.
102///
103/// # Parameters
104///
105/// * `value` - Source value to convert.
106/// * `policy` - Conversion policy forwarded to `qubit_datatype`.
107/// * `limits` - Conversion limits forwarded to `qubit_datatype`.
108///
109/// # Returns
110///
111/// Returns the converted value.
112///
113/// # Errors
114///
115/// Returns a `ValueError` mapped from the shared conversion error when the
116/// source value is missing, unsupported, or invalid for `T`.
117#[inline]
118pub(in crate::value) fn convert_with_data_converter_with<T>(
119 value: &Value,
120 policy: &ConversionPolicy,
121 limits: &ConversionLimits,
122) -> ValueResult<T>
123where
124 T: DataConversionTarget,
125{
126 data_converter_from_value(value)
127 .to_with::<T>(policy, limits)
128 .map_err(|error| contextual_conversion_error(value, error))
129}
130
131/// Converts a single `Value` into `T` using an existing conversion session.
132///
133/// Charges the session item budget before input checks and target conversion.
134/// Successfully charged items and input bytes remain consumed if a later
135/// check or target conversion fails. A rejected item charge consumes no item;
136/// a rejected input-byte charge consumes no bytes and preserves earlier
137/// charges.
138///
139/// # Type Parameters
140///
141/// * `T` - Target type supported by the shared conversion layer.
142///
143/// # Parameters
144///
145/// * `value` - Source runtime value.
146/// * `session` - Caller-owned session providing policy, limits, and budget.
147///
148/// # Returns
149///
150/// The converted target value.
151///
152/// # Errors
153///
154/// Returns a mapped missing, conversion, or budget error.
155#[inline]
156pub(in crate::value) fn convert_with_data_converter_in<T>(
157 value: &Value,
158 session: &mut ConversionSession<'_>,
159) -> ValueResult<T>
160where
161 T: DataConversionTarget,
162{
163 data_converter_from_value(value)
164 .to_in::<T>(session)
165 .map_err(|error| contextual_conversion_error(value, error))
166}
167
168/// Preserves scalar storage facts after the converter has completed admission.
169///
170/// # Parameters
171///
172/// * `value` - Source value whose unset storage facts contextualize missing
173/// errors.
174/// * `error` - Completed conversion failure, consumed into the runtime error.
175///
176/// # Returns
177///
178/// The mapped error, with storage type and unset-scalar reason attached only
179/// when a missing-value error originated from an unset source.
180#[inline]
181fn contextual_conversion_error(value: &Value, error: DataConversionError) -> ValueError {
182 match ValueError::from(error) {
183 ValueError::Missing(missing) if value.is_unset() => ValueError::Missing(
184 missing.with_storage(value.data_type(), ValueMissingReason::UnsetScalar),
185 ),
186 error => error,
187 }
188}