1use api::v1::ColumnDataType;
16use api::v1::value::ValueData;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum TraceCoerceError {
20 Unsupported,
21}
22
23pub fn is_supported_trace_coercion(
34 request_type: ColumnDataType,
35 target_type: ColumnDataType,
36) -> bool {
37 matches!(
38 (request_type, target_type),
39 (ColumnDataType::Int64, ColumnDataType::Float64)
40 | (ColumnDataType::Int64, ColumnDataType::String)
41 | (ColumnDataType::Float64, ColumnDataType::String)
42 | (ColumnDataType::Boolean, ColumnDataType::String)
43 | (ColumnDataType::String, ColumnDataType::Int64)
44 | (ColumnDataType::String, ColumnDataType::Float64)
45 | (ColumnDataType::String, ColumnDataType::Boolean)
46 )
47}
48
49pub fn coerce_value_data(
50 value: &Option<ValueData>,
51 target: ColumnDataType,
52 request_type: ColumnDataType,
53) -> Result<Option<ValueData>, TraceCoerceError> {
54 let Some(v) = value else {
55 return Ok(None);
56 };
57
58 let Some(value) = coerce_non_null_value(target, request_type, v) else {
59 return Err(TraceCoerceError::Unsupported);
60 };
61 Ok(Some(value))
62}
63
64pub fn coerce_non_null_value(
65 target: ColumnDataType,
66 request_type: ColumnDataType,
67 value: &ValueData,
68) -> Option<ValueData> {
69 match (request_type, target, value) {
70 (ColumnDataType::Int64, ColumnDataType::Float64, ValueData::I64Value(n)) => {
71 Some(ValueData::F64Value(*n as f64))
72 }
73 (ColumnDataType::Int64, ColumnDataType::String, ValueData::I64Value(n)) => {
74 Some(ValueData::StringValue(n.to_string()))
75 }
76 (ColumnDataType::Float64, ColumnDataType::String, ValueData::F64Value(n)) => {
77 Some(ValueData::StringValue(n.to_string()))
78 }
79 (ColumnDataType::Boolean, ColumnDataType::String, ValueData::BoolValue(b)) => {
80 Some(ValueData::StringValue(b.to_string()))
81 }
82 (ColumnDataType::String, ColumnDataType::Int64, ValueData::StringValue(s)) => {
83 s.parse::<i64>().ok().map(ValueData::I64Value)
84 }
85 (ColumnDataType::String, ColumnDataType::Float64, ValueData::StringValue(s)) => {
86 s.parse::<f64>().ok().map(ValueData::F64Value)
87 }
88 (ColumnDataType::String, ColumnDataType::Boolean, ValueData::StringValue(s)) => {
89 s.parse::<bool>().ok().map(ValueData::BoolValue)
90 }
91 _ => None,
92 }
93}
94
95pub fn trace_value_datatype(value: &ValueData) -> Option<ColumnDataType> {
96 match value {
97 ValueData::StringValue(_) => Some(ColumnDataType::String),
98 ValueData::BoolValue(_) => Some(ColumnDataType::Boolean),
99 ValueData::I64Value(_) => Some(ColumnDataType::Int64),
100 ValueData::F64Value(_) => Some(ColumnDataType::Float64),
101 ValueData::BinaryValue(_) => Some(ColumnDataType::Binary),
102 _ => None,
103 }
104}
105
106pub fn resolve_new_trace_column_type(
109 observed_types: impl IntoIterator<Item = ColumnDataType>,
110) -> Result<Option<ColumnDataType>, TraceCoerceError> {
111 let mut observed_types = observed_types.into_iter().collect::<Vec<_>>();
112 observed_types.dedup();
113
114 match observed_types.as_slice() {
115 [] => Ok(None),
116 [datatype] => Ok(Some(*datatype)),
117 [_, _]
118 if observed_types.contains(&ColumnDataType::String)
119 && observed_types.contains(&ColumnDataType::Boolean) =>
120 {
121 Ok(Some(ColumnDataType::Boolean))
122 }
123 [_, _]
124 if observed_types.contains(&ColumnDataType::String)
125 && observed_types.contains(&ColumnDataType::Int64) =>
126 {
127 Ok(Some(ColumnDataType::Int64))
128 }
129 [_, _]
130 if observed_types.contains(&ColumnDataType::String)
131 && observed_types.contains(&ColumnDataType::Float64) =>
132 {
133 Ok(Some(ColumnDataType::Float64))
134 }
135 [_, _]
136 if observed_types.contains(&ColumnDataType::Int64)
137 && observed_types.contains(&ColumnDataType::Float64) =>
138 {
139 Ok(Some(ColumnDataType::Float64))
140 }
141 _ => Err(TraceCoerceError::Unsupported),
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_coerce_int64_to_float64() {
151 let result = coerce_value_data(
152 &Some(ValueData::I64Value(42)),
153 ColumnDataType::Float64,
154 ColumnDataType::Int64,
155 );
156 assert_eq!(result, Ok(Some(ValueData::F64Value(42.0))));
157 }
158
159 #[test]
160 fn test_coerce_string_to_int64() {
161 let result = coerce_value_data(
162 &Some(ValueData::StringValue("123".to_string())),
163 ColumnDataType::Int64,
164 ColumnDataType::String,
165 );
166 assert_eq!(result, Ok(Some(ValueData::I64Value(123))));
167 }
168
169 #[test]
170 fn test_coerce_int64_to_string() {
171 let result = coerce_value_data(
172 &Some(ValueData::I64Value(123)),
173 ColumnDataType::String,
174 ColumnDataType::Int64,
175 );
176 assert_eq!(result, Ok(Some(ValueData::StringValue("123".to_string()))));
177 }
178
179 #[test]
180 fn test_coerce_string_to_float64() {
181 let result = coerce_value_data(
182 &Some(ValueData::StringValue("1.5".to_string())),
183 ColumnDataType::Float64,
184 ColumnDataType::String,
185 );
186 assert_eq!(result, Ok(Some(ValueData::F64Value(1.5))));
187 }
188
189 #[test]
190 fn test_coerce_float64_to_string() {
191 let result = coerce_value_data(
192 &Some(ValueData::F64Value(1.5)),
193 ColumnDataType::String,
194 ColumnDataType::Float64,
195 );
196 assert_eq!(result, Ok(Some(ValueData::StringValue("1.5".to_string()))));
197 }
198
199 #[test]
200 fn test_coerce_string_to_boolean() {
201 let result = coerce_value_data(
202 &Some(ValueData::StringValue("true".to_string())),
203 ColumnDataType::Boolean,
204 ColumnDataType::String,
205 );
206 assert_eq!(result, Ok(Some(ValueData::BoolValue(true))));
207
208 let result = coerce_value_data(
209 &Some(ValueData::StringValue("false".to_string())),
210 ColumnDataType::Boolean,
211 ColumnDataType::String,
212 );
213 assert_eq!(result, Ok(Some(ValueData::BoolValue(false))));
214 }
215
216 #[test]
217 fn test_coerce_boolean_to_string() {
218 let result = coerce_value_data(
219 &Some(ValueData::BoolValue(true)),
220 ColumnDataType::String,
221 ColumnDataType::Boolean,
222 );
223 assert_eq!(result, Ok(Some(ValueData::StringValue("true".to_string()))));
224 }
225
226 #[test]
227 fn test_coerce_unparsable_string() {
228 let result = coerce_value_data(
229 &Some(ValueData::StringValue("not_a_number".to_string())),
230 ColumnDataType::Int64,
231 ColumnDataType::String,
232 );
233 assert_eq!(result, Err(TraceCoerceError::Unsupported));
234 }
235
236 #[test]
237 fn test_coerce_float64_to_int64_not_supported() {
238 let result = coerce_value_data(
239 &Some(ValueData::F64Value(1.5)),
240 ColumnDataType::Int64,
241 ColumnDataType::Float64,
242 );
243 assert_eq!(result, Err(TraceCoerceError::Unsupported));
244 }
245
246 #[test]
247 fn test_coerce_none_value() {
248 let result = coerce_value_data(&None, ColumnDataType::Float64, ColumnDataType::Int64);
249 assert_eq!(result, Ok(None));
250 }
251
252 #[test]
253 fn test_is_supported_trace_coercion() {
254 assert!(is_supported_trace_coercion(
255 ColumnDataType::Int64,
256 ColumnDataType::Float64
257 ));
258 assert!(is_supported_trace_coercion(
259 ColumnDataType::Int64,
260 ColumnDataType::String
261 ));
262 assert!(is_supported_trace_coercion(
263 ColumnDataType::Float64,
264 ColumnDataType::String
265 ));
266 assert!(is_supported_trace_coercion(
267 ColumnDataType::Boolean,
268 ColumnDataType::String
269 ));
270 assert!(is_supported_trace_coercion(
271 ColumnDataType::String,
272 ColumnDataType::Int64
273 ));
274 assert!(is_supported_trace_coercion(
275 ColumnDataType::String,
276 ColumnDataType::Float64
277 ));
278 assert!(is_supported_trace_coercion(
279 ColumnDataType::String,
280 ColumnDataType::Boolean
281 ));
282 assert!(!is_supported_trace_coercion(
283 ColumnDataType::Binary,
284 ColumnDataType::Json
285 ));
286 }
287
288 #[test]
289 fn test_trace_value_datatype() {
290 assert_eq!(
291 trace_value_datatype(&ValueData::StringValue("x".to_string())),
292 Some(ColumnDataType::String)
293 );
294 assert_eq!(
295 trace_value_datatype(&ValueData::BoolValue(true)),
296 Some(ColumnDataType::Boolean)
297 );
298 assert_eq!(
299 trace_value_datatype(&ValueData::I64Value(1)),
300 Some(ColumnDataType::Int64)
301 );
302 assert_eq!(
303 trace_value_datatype(&ValueData::F64Value(1.0)),
304 Some(ColumnDataType::Float64)
305 );
306 assert_eq!(
307 trace_value_datatype(&ValueData::BinaryValue(vec![1_u8])),
308 Some(ColumnDataType::Binary)
309 );
310 }
311
312 #[test]
313 fn test_resolve_new_trace_column_type() {
314 assert_eq!(
315 resolve_new_trace_column_type([ColumnDataType::Int64]),
316 Ok(Some(ColumnDataType::Int64))
317 );
318 assert_eq!(
319 resolve_new_trace_column_type([ColumnDataType::String, ColumnDataType::Int64]),
320 Ok(Some(ColumnDataType::Int64))
321 );
322 assert_eq!(
323 resolve_new_trace_column_type([ColumnDataType::String, ColumnDataType::Float64]),
324 Ok(Some(ColumnDataType::Float64))
325 );
326 assert_eq!(
327 resolve_new_trace_column_type([ColumnDataType::String, ColumnDataType::Boolean]),
328 Ok(Some(ColumnDataType::Boolean))
329 );
330 assert_eq!(
331 resolve_new_trace_column_type([ColumnDataType::Int64, ColumnDataType::Float64]),
332 Ok(Some(ColumnDataType::Float64))
333 );
334 assert_eq!(
335 resolve_new_trace_column_type([
336 ColumnDataType::String,
337 ColumnDataType::Int64,
338 ColumnDataType::Float64,
339 ]),
340 Err(TraceCoerceError::Unsupported)
341 );
342 }
343}