1use crate::types::Value;
12use chrono::DateTime;
13use std::net::{Ipv4Addr, Ipv6Addr};
14
15pub struct ValueFormatter;
17
18impl ValueFormatter {
19 pub fn format_value(value: &Value) -> String {
29 match value {
30 Value::Null => "null".to_string(),
31
32 Value::Boolean(b) => b.to_string(),
34
35 Value::TinyInt(i) => i.to_string(),
37 Value::SmallInt(i) => i.to_string(),
38 Value::Integer(i) => i.to_string(),
39 Value::BigInt(i) => i.to_string(),
40 Value::Counter(i) => i.to_string(),
41
42 Value::Float32(f) => Self::format_float32(*f),
44 Value::Float(f) => Self::format_float64(*f),
45
46 Value::Text(s) => s.clone(),
48
49 Value::Blob(bytes) => format!("0x{}", hex::encode(bytes)),
51
52 Value::Timestamp(millis) => Self::format_timestamp(*millis),
54
55 Value::Date(days) => Self::format_date(*days),
57
58 Value::Time(nanos) => Self::format_time(*nanos),
60
61 Value::Uuid(bytes) => Self::format_uuid(bytes),
63
64 Value::Varint(bytes) => Self::format_varint(bytes),
66
67 Value::Decimal { scale, unscaled } => Self::format_decimal(*scale, unscaled),
69
70 Value::Duration {
72 months,
73 days,
74 nanos,
75 } => Self::format_duration(*months, *days, *nanos),
76
77 Value::Json(json_value) => json_value.to_string(),
79
80 Value::List(elements) => Self::format_list(elements),
82 Value::Set(elements) => Self::format_set(elements),
83 Value::Map(pairs) => Self::format_map(pairs),
84 Value::Tuple(fields) => Self::format_tuple(fields),
85
86 Value::Udt(udt) => Self::format_udt(udt),
88
89 Value::Frozen(inner) => Self::format_value(inner),
91
92 Value::Tombstone(info) => format!("<deleted@{}>", info.deletion_time),
94
95 Value::Inet(bytes) => Self::format_inet(bytes),
97 }
98 }
99
100 fn format_float32(f: f32) -> String {
104 if f.is_nan() {
105 "NaN".to_string()
106 } else if f.is_infinite() {
107 if f.is_sign_positive() {
108 "Infinity".to_string()
109 } else {
110 "-Infinity".to_string()
111 }
112 } else if f.abs() < 1e-6 || f.abs() > 1e10 {
113 format!("{:e}", f)
114 } else {
115 format!("{}", f)
116 }
117 }
118
119 fn format_float64(f: f64) -> String {
121 if f.is_nan() {
122 "NaN".to_string()
123 } else if f.is_infinite() {
124 if f.is_sign_positive() {
125 "Infinity".to_string()
126 } else {
127 "-Infinity".to_string()
128 }
129 } else if f.abs() < 1e-6 || f.abs() > 1e10 {
130 format!("{:e}", f)
131 } else {
132 format!("{}", f)
133 }
134 }
135
136 fn format_timestamp(millis: i64) -> String {
138 if let Some(datetime) = DateTime::from_timestamp_millis(millis) {
141 datetime.format("%Y-%m-%d %H:%M:%S%.3f+0000").to_string()
143 } else {
144 format!("<invalid-timestamp:{}>", millis)
145 }
146 }
147
148 fn format_date(days: i32) -> String {
150 let epoch = DateTime::from_timestamp(0, 0)
152 .map(|dt| dt.date_naive())
153 .unwrap_or_else(|| {
154 chrono::NaiveDate::from_ymd_opt(1970, 1, 1).unwrap_or(chrono::NaiveDate::MIN)
157 });
158
159 if let Some(date) = epoch.checked_add_signed(chrono::Duration::days(days as i64)) {
160 date.format("%Y-%m-%d").to_string()
161 } else {
162 format!("<invalid-date:{}>", days)
163 }
164 }
165
166 fn format_time(nanos: i64) -> String {
168 if nanos < 0 {
169 return format!("<invalid-time:{}>", nanos);
170 }
171
172 let total_secs = nanos / 1_000_000_000;
173 let hours = total_secs / 3600;
174 let minutes = (total_secs % 3600) / 60;
175 let seconds = total_secs % 60;
176 let remaining_nanos = nanos % 1_000_000_000;
177
178 if hours >= 24 {
179 return format!("<invalid-time:{}>", nanos);
180 }
181
182 format!(
183 "{:02}:{:02}:{:02}.{:09}",
184 hours, minutes, seconds, remaining_nanos
185 )
186 }
187
188 fn format_uuid(bytes: &[u8; 16]) -> String {
190 format!(
191 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
192 bytes[0], bytes[1], bytes[2], bytes[3],
193 bytes[4], bytes[5],
194 bytes[6], bytes[7],
195 bytes[8], bytes[9],
196 bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
197 )
198 }
199
200 fn format_varint(bytes: &[u8]) -> String {
202 if bytes.is_empty() {
203 return "0".to_string();
204 }
205
206 let result = num_bigint::BigInt::from_signed_bytes_be(bytes);
208 result.to_string()
209 }
210
211 fn format_decimal(scale: i32, unscaled: &[u8]) -> String {
213 if unscaled.is_empty() {
214 return "0".to_string();
215 }
216
217 let is_negative = (unscaled[0] & 0x80) != 0;
219 let bigint = if is_negative {
220 num_bigint::BigInt::from_signed_bytes_be(unscaled)
222 } else {
223 num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, unscaled)
224 };
225
226 let mut decimal_str = bigint.to_string();
227 let is_neg = decimal_str.starts_with('-');
228 if is_neg {
229 decimal_str = decimal_str[1..].to_string();
230 }
231
232 if scale <= 0 {
234 decimal_str.push_str(&"0".repeat((-scale) as usize));
236 } else if scale as usize >= decimal_str.len() {
237 let leading_zeros = scale as usize - decimal_str.len() + 1;
239 decimal_str = format!("0.{}{}", "0".repeat(leading_zeros - 1), decimal_str);
240 } else {
241 let pos = decimal_str.len() - scale as usize;
243 decimal_str.insert(pos, '.');
244 }
245
246 if is_neg {
247 format!("-{}", decimal_str)
248 } else {
249 decimal_str
250 }
251 }
252
253 fn format_duration(months: i32, days: i32, nanos: i64) -> String {
255 let mut parts = Vec::new();
256
257 if months != 0 {
258 parts.push(format!("{}mo", months));
259 }
260 if days != 0 {
261 parts.push(format!("{}d", days));
262 }
263 if nanos != 0 {
264 parts.push(format!("{}ns", nanos));
265 }
266
267 if parts.is_empty() {
268 "0ns".to_string()
269 } else {
270 parts.join("")
271 }
272 }
273
274 fn format_list(elements: &[Value]) -> String {
276 let formatted_elements: Vec<String> = elements.iter().map(Self::format_value).collect();
277 format!("[{}]", formatted_elements.join(", "))
278 }
279
280 fn format_set(elements: &[Value]) -> String {
282 let formatted_elements: Vec<String> = elements.iter().map(Self::format_value).collect();
283 format!("{{{}}}", formatted_elements.join(", "))
284 }
285
286 fn format_map(pairs: &[(Value, Value)]) -> String {
288 let formatted_pairs: Vec<String> = pairs
289 .iter()
290 .map(|(k, v)| format!("{}: {}", Self::format_value(k), Self::format_value(v)))
291 .collect();
292 format!("{{{}}}", formatted_pairs.join(", "))
293 }
294
295 fn format_tuple(fields: &[Value]) -> String {
297 let formatted_fields: Vec<String> = fields.iter().map(Self::format_value).collect();
298 format!("({})", formatted_fields.join(", "))
299 }
300
301 fn format_udt(udt: &crate::types::UdtValue) -> String {
303 let formatted_fields: Vec<String> = udt
304 .fields
305 .iter()
306 .map(|field| {
307 let value_str = field
308 .value
309 .as_ref()
310 .map(Self::format_value)
311 .unwrap_or_else(|| "null".to_string());
312 format!("{}: {}", field.name, value_str)
313 })
314 .collect();
315 format!("{{{}}}", formatted_fields.join(", "))
316 }
317
318 fn format_inet(bytes: &[u8]) -> String {
320 if bytes.len() == 4 {
321 let addr = Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]);
323 addr.to_string()
324 } else if bytes.len() == 16 {
325 let mut octets = [0u8; 16];
327 octets.copy_from_slice(bytes);
328 let addr = Ipv6Addr::from(octets);
329 addr.to_string()
330 } else {
331 format!("<invalid-inet:{}-bytes>", bytes.len())
332 }
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339 use crate::types::{UdtField, UdtValue};
340
341 #[test]
342 fn test_null() {
343 assert_eq!(ValueFormatter::format_value(&Value::Null), "null");
344 }
345
346 #[test]
347 fn test_boolean() {
348 assert_eq!(ValueFormatter::format_value(&Value::Boolean(true)), "true");
349 assert_eq!(
350 ValueFormatter::format_value(&Value::Boolean(false)),
351 "false"
352 );
353 }
354
355 #[test]
356 fn test_integers() {
357 assert_eq!(ValueFormatter::format_value(&Value::TinyInt(127)), "127");
358 assert_eq!(ValueFormatter::format_value(&Value::TinyInt(-128)), "-128");
359 assert_eq!(
360 ValueFormatter::format_value(&Value::SmallInt(32767)),
361 "32767"
362 );
363 assert_eq!(
364 ValueFormatter::format_value(&Value::Integer(2147483647)),
365 "2147483647"
366 );
367 assert_eq!(
368 ValueFormatter::format_value(&Value::BigInt(9223372036854775807)),
369 "9223372036854775807"
370 );
371 assert_eq!(
372 ValueFormatter::format_value(&Value::Counter(1000000)),
373 "1000000"
374 );
375 }
376
377 #[test]
378 fn test_floats() {
379 assert_eq!(ValueFormatter::format_value(&Value::Float32(3.25)), "3.25");
380 assert_eq!(ValueFormatter::format_value(&Value::Float(2.75)), "2.75");
381
382 assert_eq!(
384 ValueFormatter::format_value(&Value::Float32(f32::NAN)),
385 "NaN"
386 );
387 assert_eq!(
388 ValueFormatter::format_value(&Value::Float32(f32::INFINITY)),
389 "Infinity"
390 );
391 assert_eq!(
392 ValueFormatter::format_value(&Value::Float32(f32::NEG_INFINITY)),
393 "-Infinity"
394 );
395
396 let small = Value::Float(1e-7);
398 let formatted = ValueFormatter::format_value(&small);
399 assert!(formatted.contains('e') || formatted.contains('E'));
400 }
401
402 #[test]
403 fn test_text() {
404 assert_eq!(
405 ValueFormatter::format_value(&Value::Text("hello world".to_string())),
406 "hello world"
407 );
408 assert_eq!(
409 ValueFormatter::format_value(&Value::Text("".to_string())),
410 ""
411 );
412 }
413
414 #[test]
415 fn test_blob() {
416 let blob = Value::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF]);
417 assert_eq!(ValueFormatter::format_value(&blob), "0xdeadbeef");
418
419 let empty_blob = Value::Blob(vec![]);
420 assert_eq!(ValueFormatter::format_value(&empty_blob), "0x");
421 }
422
423 #[test]
424 fn test_uuid() {
425 let uuid = Value::Uuid([
427 0xa8, 0xf1, 0x67, 0xf0, 0xeb, 0xe7, 0x4f, 0x20, 0xa3, 0x86, 0x31, 0xff, 0x13, 0x8b,
428 0xec, 0x3b,
429 ]);
430 assert_eq!(
431 ValueFormatter::format_value(&uuid),
432 "a8f167f0-ebe7-4f20-a386-31ff138bec3b"
433 );
434 }
435
436 #[test]
437 fn test_timestamp() {
438 let timestamp = Value::Timestamp(1673778645123);
440 let formatted = ValueFormatter::format_value(×tamp);
441 assert!(formatted.starts_with("2023-01-15"));
442 assert!(formatted.contains("10:30:45"));
443 assert!(formatted.ends_with("+0000"));
444 }
445
446 #[test]
447 fn test_date() {
448 let date = Value::Date(19358);
450 assert_eq!(ValueFormatter::format_value(&date), "2023-01-01");
451
452 let epoch = Value::Date(0);
454 assert_eq!(ValueFormatter::format_value(&epoch), "1970-01-01");
455 }
456
457 #[test]
458 fn test_time() {
459 let nanos =
461 14 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000 + 45 * 1_000_000_000 + 123_456_789;
462 let time = Value::Time(nanos);
463 assert_eq!(ValueFormatter::format_value(&time), "14:30:45.123456789");
464
465 let midnight = Value::Time(0);
467 assert_eq!(
468 ValueFormatter::format_value(&midnight),
469 "00:00:00.000000000"
470 );
471 }
472
473 #[test]
474 fn test_duration() {
475 let duration = Value::Duration {
476 months: 2,
477 days: 15,
478 nanos: 123456789,
479 };
480 assert_eq!(ValueFormatter::format_value(&duration), "2mo15d123456789ns");
481
482 let zero_duration = Value::Duration {
483 months: 0,
484 days: 0,
485 nanos: 0,
486 };
487 assert_eq!(ValueFormatter::format_value(&zero_duration), "0ns");
488
489 let partial_duration = Value::Duration {
490 months: 0,
491 days: 5,
492 nanos: 0,
493 };
494 assert_eq!(ValueFormatter::format_value(&partial_duration), "5d");
495 }
496
497 #[test]
498 fn test_list() {
499 let list = Value::List(vec![
500 Value::Integer(1),
501 Value::Integer(2),
502 Value::Integer(3),
503 ]);
504 assert_eq!(ValueFormatter::format_value(&list), "[1, 2, 3]");
505
506 let empty_list = Value::List(vec![]);
507 assert_eq!(ValueFormatter::format_value(&empty_list), "[]");
508 }
509
510 #[test]
511 fn test_set() {
512 let set = Value::Set(vec![
513 Value::Text("apple".to_string()),
514 Value::Text("banana".to_string()),
515 ]);
516 assert_eq!(ValueFormatter::format_value(&set), "{apple, banana}");
517
518 let empty_set = Value::Set(vec![]);
519 assert_eq!(ValueFormatter::format_value(&empty_set), "{}");
520 }
521
522 #[test]
523 fn test_map() {
524 let map = Value::Map(vec![
525 (Value::Text("key1".to_string()), Value::Integer(100)),
526 (Value::Text("key2".to_string()), Value::Integer(200)),
527 ]);
528 assert_eq!(ValueFormatter::format_value(&map), "{key1: 100, key2: 200}");
529
530 let empty_map = Value::Map(vec![]);
531 assert_eq!(ValueFormatter::format_value(&empty_map), "{}");
532 }
533
534 #[test]
535 fn test_tuple() {
536 let tuple = Value::Tuple(vec![
537 Value::Integer(42),
538 Value::Text("hello".to_string()),
539 Value::Boolean(true),
540 ]);
541 assert_eq!(ValueFormatter::format_value(&tuple), "(42, hello, true)");
542 }
543
544 #[test]
545 fn test_udt() {
546 let udt = Value::Udt(UdtValue {
547 type_name: "person".to_string(),
548 keyspace: "test_ks".to_string(),
549 fields: vec![
550 UdtField {
551 name: "name".to_string(),
552 value: Some(Value::Text("Alice".to_string())),
553 },
554 UdtField {
555 name: "age".to_string(),
556 value: Some(Value::Integer(30)),
557 },
558 UdtField {
559 name: "email".to_string(),
560 value: None,
561 },
562 ],
563 });
564 assert_eq!(
565 ValueFormatter::format_value(&udt),
566 "{name: Alice, age: 30, email: null}"
567 );
568 }
569
570 #[test]
571 fn test_frozen() {
572 let frozen = Value::Frozen(Box::new(Value::List(vec![
573 Value::Integer(1),
574 Value::Integer(2),
575 ])));
576 assert_eq!(ValueFormatter::format_value(&frozen), "[1, 2]");
577 }
578
579 #[test]
580 fn test_inet() {
581 let ipv4 = Value::Inet(vec![192, 168, 1, 1]);
583 assert_eq!(ValueFormatter::format_value(&ipv4), "192.168.1.1");
584
585 let ipv6 = Value::Inet(vec![
587 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
588 0x00, 0x01,
589 ]);
590 let formatted = ValueFormatter::format_value(&ipv6);
591 assert!(formatted.contains("2001:db8"));
592 }
593
594 #[test]
595 fn test_nested_collections() {
596 let nested = Value::List(vec![
598 Value::List(vec![Value::Integer(1), Value::Integer(2)]),
599 Value::List(vec![Value::Integer(3), Value::Integer(4)]),
600 ]);
601 assert_eq!(ValueFormatter::format_value(&nested), "[[1, 2], [3, 4]]");
602
603 let complex_map = Value::Map(vec![(
605 Value::Text("data".to_string()),
606 Value::Set(vec![Value::Integer(1), Value::Integer(2)]),
607 )]);
608 assert_eq!(ValueFormatter::format_value(&complex_map), "{data: {1, 2}}");
609 }
610
611 #[test]
612 fn test_json() {
613 let json = Value::Json(serde_json::json!({
614 "name": "Alice",
615 "age": 30
616 }));
617 let formatted = ValueFormatter::format_value(&json);
618 assert!(formatted.contains("Alice"));
619 assert!(formatted.contains("30"));
620 }
621
622 #[test]
623 fn test_varint() {
624 let varint = Value::Varint(vec![0x01, 0x00]);
626 let formatted = ValueFormatter::format_value(&varint);
627 assert_eq!(formatted, "256");
628
629 let zero = Value::Varint(vec![]);
631 assert_eq!(ValueFormatter::format_value(&zero), "0");
632 }
633
634 #[test]
635 fn test_decimal() {
636 let decimal = Value::Decimal {
638 scale: 2,
639 unscaled: vec![0x30, 0x39], };
641 let formatted = ValueFormatter::format_value(&decimal);
642 assert!(formatted.contains('.'));
644 }
645
646 #[test]
647 fn test_format_varint_negative() {
648 let negative_bytes = vec![0xFF];
650 let formatted = ValueFormatter::format_value(&Value::Varint(negative_bytes));
651 assert_eq!(
652 formatted, "-1",
653 "Negative varint -1 should format correctly"
654 );
655
656 let negative_256 = vec![0xFF, 0x00];
658 let formatted_256 = ValueFormatter::format_value(&Value::Varint(negative_256));
659 assert_eq!(
660 formatted_256, "-256",
661 "Negative varint -256 should format correctly"
662 );
663
664 assert!(!formatted.contains('<'), "Should not contain debug markers");
666 assert!(!formatted.contains('>'), "Should not contain debug markers");
667 }
668}