1use std::str::FromStr;
12
13use qubit_budget::json::JsonMeasurement;
14use qubit_datatype::ConversionLimits;
15use qubit_datatype::ConversionPolicy;
16use qubit_datatype::DataConversionError;
17use qubit_datatype::DataConverter;
18use qubit_datatype::DataListConversionError;
19use qubit_datatype::DataType;
20use qubit_datatype::InvalidValueReason;
21use serde_json::Number;
22use serde_json::Value as JsonValue;
23
24use crate::MultiValues;
25use crate::MultiValuesRef;
26use crate::Value;
27use crate::ValueContainer;
28use crate::ValueError;
29use crate::ValueRef;
30use crate::ValueResult;
31
32mod json_children;
33mod measurement_writer;
34mod prepared_projection;
35mod prepared_scalar;
36mod projection_budget;
37
38use json_children::JsonChildren;
39use prepared_projection::PreparedProjection;
40use prepared_scalar::PreparedScalar;
41use projection_budget::ProjectionBudget;
42
43macro_rules! check_projection_number {
45 (BigInteger, $value:expr, $budget:expr) => {
46 $budget
47 .limits
48 .numeric()
49 .big_integer()
50 .check($value)
51 .map_err(|error| $budget.error(error))?
52 };
53 (BigDecimal, $value:expr, $budget:expr) => {
54 $budget
55 .limits
56 .numeric()
57 .big_decimal()
58 .check($value)
59 .map_err(|error| $budget.error(error))?
60 };
61 ($variant:ident, $value:expr, $budget:expr) => {};
62}
63
64macro_rules! cache_projection {
66 (String, $class:ident) => {
67 false
68 };
69 ($variant:ident, json_bool) => {
70 false
71 };
72 ($variant:ident, json_number) => {
73 false
74 };
75 ($variant:ident, json_object) => {
76 false
77 };
78 ($variant:ident, json_identity) => {
79 false
80 };
81 ($variant:ident, $class:ident) => {
82 true
83 };
84}
85
86macro_rules! prepare_payload {
88 (String, $class:ident, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
89 $budget.input($value)?;
90 $budget.admit_output_string($value.len(), $depth)?;
91 Ok(PreparedScalar::Borrowed($view))
92 }};
93 ($variant:ident, json_bool, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
94 let _ = $value;
95 $budget.admit(JsonMeasurement::Boolean { depth: $depth })?;
96 Ok(PreparedScalar::Borrowed($view))
97 }};
98 ($variant:ident, json_number, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
99 $budget.display(&$value, $depth, true)?;
100 Ok(PreparedScalar::Borrowed($view))
101 }};
102 ($variant:ident, json_float32, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
103 let number = Number::from_str(&$value.to_string())
104 .map_err(|_| DataConversionError::invalid($from, DataType::Json, InvalidValueReason::NonFinite))?;
105 $budget.display(&number, $depth, true)?;
106 Ok(PreparedScalar::Number(number))
107 }};
108 ($variant:ident, json_float64, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
109 let number = Number::from_f64($value)
110 .ok_or_else(|| DataConversionError::invalid($from, DataType::Json, InvalidValueReason::NonFinite))?;
111 $budget.display(&number, $depth, true)?;
112 Ok(PreparedScalar::Number(number))
113 }};
114 ($variant:ident, json_string, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {
115 $budget.format(&$value, $depth).map(PreparedScalar::Formatted)
116 };
117 ($variant:ident, json_duration, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
118 let text = DataConverter::from($value).to_in::<String>(&mut $budget.conversion)?;
119 $budget.admit_output_string(text.len(), $depth)?;
120 Ok(PreparedScalar::Formatted(text))
121 }};
122 ($variant:ident, json_object, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
123 $budget.admit(JsonMeasurement::Object {
124 depth: $depth,
125 entries: $value.len(),
126 })?;
127 for (key, value) in $value {
128 $budget.text(key, $depth, true)?;
129 $budget.text(value, $depth.saturating_add(1), false)?;
130 }
131 Ok(PreparedScalar::Borrowed($view))
132 }};
133 ($variant:ident, json_identity, $value:expr, $view:expr, $from:expr, $budget:expr, $depth:expr) => {{
134 admit_json($value, $depth, $budget)?;
135 Ok(PreparedScalar::Borrowed($view))
136 }};
137}
138
139macro_rules! prepare_scalar_match {
141 ($view:expr, $budget:expr, $depth: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)*)),+ $(,)?) => {
142 match $view {
143 ValueRef::Unset(_) => {
144 $budget.admit(JsonMeasurement::Null { depth: $depth })?;
145 Ok(PreparedScalar::Borrowed($view))
146 }
147 $($(#[$cfg])* ValueRef::$variant(value) => {
148 check_projection_number!($variant, value, $budget);
149 prepare_payload!($variant, $json_class, value, $view, $data_type, $budget, $depth)
150 },)+
151 }
152 };
153}
154
155fn prepare_scalar<'a>(
157 view: ValueRef<'a>,
158 budget: &mut ProjectionBudget<'_>,
159 depth: usize,
160) -> ValueResult<PreparedScalar<'a>> {
161 budget.item()?;
162 for_each_value_type!(prepare_scalar_match, view, budget, depth)
163}
164
165macro_rules! collection_cache_match {
167 ($view: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)*)),+ $(,)?) => {
168 match $view {
169 MultiValuesRef::Unset(_) => false,
170 $($(#[$cfg])* MultiValuesRef::$variant(_) => cache_projection!($variant, $json_class),)+
171 }
172 };
173}
174
175fn prepare_collection<'a>(
177 view: MultiValuesRef<'a>,
178 budget: &mut ProjectionBudget<'_>,
179) -> ValueResult<PreparedProjection<'a>> {
180 if matches!(view, MultiValuesRef::Unset(_)) {
181 budget.item()?;
182 budget.admit(JsonMeasurement::Null { depth: 1 })?;
183 return Ok(PreparedProjection::BorrowedCollection(view));
184 }
185 budget.admit(JsonMeasurement::Array {
186 depth: 1,
187 items: view.len(),
188 })?;
189 let cache = for_each_value_type!(collection_cache_match, view);
190 let mut prepared = Vec::new();
191 for index in 0..view.len() {
192 budget.source_index = Some(index);
193 let item = view.get(index).expect("index is inside the source collection");
194 let scalar = prepare_scalar(item, budget, 2).map_err(|error| match error {
195 ValueError::Conversion(source) => DataListConversionError::new(index, source).into(),
196 error => error,
197 })?;
198 if cache {
199 prepared.push(scalar);
200 }
201 }
202 Ok(if cache {
203 PreparedProjection::Collection(prepared)
204 } else {
205 PreparedProjection::BorrowedCollection(view)
206 })
207}
208
209fn admit_json(value: &JsonValue, depth: usize, budget: &mut ProjectionBudget<'_>) -> ValueResult<()> {
212 let mut frames = Vec::<JsonChildren<'_>>::new();
213 let mut next = Some((None, value, depth));
214 while let Some((key, value, depth)) = next.take() {
215 if let Some(key) = key {
216 budget.text(key, depth, true)?;
217 }
218 match value {
219 JsonValue::Null => budget.admit(JsonMeasurement::Null { depth })?,
220 JsonValue::Bool(_) => budget.admit(JsonMeasurement::Boolean { depth })?,
221 JsonValue::Number(value) => budget.display(value, depth, true)?,
222 JsonValue::String(value) => budget.text(value, depth, false)?,
223 JsonValue::Array(values) => {
224 budget.admit(JsonMeasurement::Array {
225 depth,
226 items: values.len(),
227 })?;
228 frames.push(JsonChildren::Array(values.iter(), depth.saturating_add(1)));
229 }
230 JsonValue::Object(values) => {
231 budget.admit(JsonMeasurement::Object {
232 depth,
233 entries: values.len(),
234 })?;
235 frames.push(JsonChildren::Object(values.iter(), depth.saturating_add(1)));
236 }
237 }
238 while let Some(frame) = frames.last_mut() {
239 if let Some(child) = frame.next() {
240 next = Some(child);
241 break;
242 }
243 frames.pop();
244 }
245 }
246 Ok(())
247}
248
249pub(crate) fn value_to_json_value_with(
253 value: &Value,
254 policy: &ConversionPolicy,
255 limits: &ConversionLimits,
256) -> ValueResult<JsonValue> {
257 let mut budget = ProjectionBudget::new(value.data_type(), policy, limits);
258 Ok(PreparedProjection::Scalar(prepare_scalar(value.view(), &mut budget, 1)?).materialize())
259}
260
261pub(crate) fn multi_values_to_json_value_with(
264 values: &MultiValues,
265 policy: &ConversionPolicy,
266 limits: &ConversionLimits,
267) -> ValueResult<JsonValue> {
268 let mut budget = ProjectionBudget::new(values.data_type(), policy, limits);
269 Ok(prepare_collection(values.view(), &mut budget)?.materialize())
270}
271
272pub(crate) fn value_container_to_json_value_with(
274 container: &ValueContainer,
275 policy: &ConversionPolicy,
276 limits: &ConversionLimits,
277) -> ValueResult<JsonValue> {
278 match container {
279 ValueContainer::Scalar(value) => value_to_json_value_with(value, policy, limits),
280 ValueContainer::Collection(values) => multi_values_to_json_value_with(values, policy, limits),
281 }
282}