Skip to main content

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() => {
184            ValueError::Missing(missing.with_storage(value.data_type(), ValueMissingReason::UnsetScalar))
185        }
186        error => error,
187    }
188}