1use std::sync::Arc;
16
17use arrow_array::{
18 builder::BinaryBuilder, Array, ArrayRef, BinaryArray, BooleanArray, FixedSizeBinaryArray,
19 Float32Array, Float64Array, Int16Array, Int32Array, Int64Array, Int8Array, LargeBinaryArray,
20 LargeStringArray, ListArray, MapArray, StringArray, UInt16Array, UInt32Array, UInt64Array,
21 UInt8Array,
22};
23use arrow_buffer::{Buffer, OffsetBuffer};
24use arrow_schema::{DataType, Field};
25
26use crate::errors::{Result, RpcError};
27
28pub trait VgiArrow: Sized {
38 fn arrow_data_type() -> DataType;
40
41 fn nullable() -> bool {
44 false
45 }
46
47 fn describe_name() -> String;
50
51 fn read(arr: &dyn Array, idx: usize) -> Result<Self>;
55
56 fn build_singleton(value: Self) -> Result<ArrayRef>;
58}
59
60fn as_array<'a, A: Array + 'static>(arr: &'a dyn Array, expected: &str) -> Result<&'a A> {
62 arr.as_any()
63 .downcast_ref::<A>()
64 .ok_or_else(|| RpcError::type_error(format!("expected {expected} array")))
65}
66
67impl VgiArrow for String {
72 fn arrow_data_type() -> DataType {
73 DataType::Utf8
74 }
75 fn describe_name() -> String {
76 "str".into()
77 }
78 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
79 if let Some(s) = arr.as_any().downcast_ref::<StringArray>() {
82 return Ok(s.value(idx).to_string());
83 }
84 if let Some(d) = arr
85 .as_any()
86 .downcast_ref::<arrow_array::DictionaryArray<arrow_array::types::Int16Type>>()
87 {
88 let key = d.keys().value(idx);
89 let values = as_array::<StringArray>(d.values().as_ref(), "Utf8 (dict values)")?;
90 return Ok(values.value(key as usize).to_string());
91 }
92 if let Some(d) = arr
93 .as_any()
94 .downcast_ref::<arrow_array::DictionaryArray<arrow_array::types::Int32Type>>()
95 {
96 let key = d.keys().value(idx);
97 let values = as_array::<StringArray>(d.values().as_ref(), "Utf8 (dict values)")?;
98 return Ok(values.value(key as usize).to_string());
99 }
100 Err(RpcError::type_error(
101 "expected Utf8 (or DictionaryArray<Int16|Int32, Utf8>) array",
102 ))
103 }
104 fn build_singleton(value: Self) -> Result<ArrayRef> {
105 Ok(Arc::new(StringArray::from(vec![value])))
106 }
107}
108
109impl VgiArrow for i64 {
110 fn arrow_data_type() -> DataType {
111 DataType::Int64
112 }
113 fn describe_name() -> String {
114 "int".into()
115 }
116 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
117 if let Some(a) = arr.as_any().downcast_ref::<Int64Array>() {
118 return Ok(a.value(idx));
119 }
120 if let Some(a) = arr.as_any().downcast_ref::<Int32Array>() {
121 return Ok(a.value(idx) as i64);
122 }
123 Err(RpcError::type_error("expected Int64/Int32 array"))
124 }
125 fn build_singleton(value: Self) -> Result<ArrayRef> {
126 Ok(Arc::new(Int64Array::from(vec![value])))
127 }
128}
129
130impl VgiArrow for i32 {
131 fn arrow_data_type() -> DataType {
132 DataType::Int32
133 }
134 fn describe_name() -> String {
135 "int".into()
136 }
137 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
138 if let Some(a) = arr.as_any().downcast_ref::<Int32Array>() {
139 return Ok(a.value(idx));
140 }
141 if let Some(a) = arr.as_any().downcast_ref::<Int64Array>() {
142 return Ok(a.value(idx) as i32);
143 }
144 Err(RpcError::type_error("expected Int32/Int64 array"))
145 }
146 fn build_singleton(value: Self) -> Result<ArrayRef> {
147 Ok(Arc::new(Int32Array::from(vec![value])))
148 }
149}
150
151macro_rules! impl_int_vgi {
155 ($t:ty, $arr:ty, $dt:expr) => {
156 impl VgiArrow for $t {
157 fn arrow_data_type() -> DataType {
158 $dt
159 }
160 fn describe_name() -> String {
161 "int".into()
162 }
163 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
164 Ok(as_array::<$arr>(arr, stringify!($t))?.value(idx))
165 }
166 fn build_singleton(value: Self) -> Result<ArrayRef> {
167 Ok(Arc::new(<$arr>::from(vec![value])))
168 }
169 }
170 };
171}
172impl_int_vgi!(i8, Int8Array, DataType::Int8);
173impl_int_vgi!(i16, Int16Array, DataType::Int16);
174impl_int_vgi!(u8, UInt8Array, DataType::UInt8);
175impl_int_vgi!(u16, UInt16Array, DataType::UInt16);
176impl_int_vgi!(u32, UInt32Array, DataType::UInt32);
177impl_int_vgi!(u64, UInt64Array, DataType::UInt64);
178
179impl VgiArrow for f64 {
180 fn arrow_data_type() -> DataType {
181 DataType::Float64
182 }
183 fn describe_name() -> String {
184 "float".into()
185 }
186 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
187 if let Some(a) = arr.as_any().downcast_ref::<Float64Array>() {
188 return Ok(a.value(idx));
189 }
190 if let Some(a) = arr.as_any().downcast_ref::<Float32Array>() {
191 return Ok(a.value(idx) as f64);
192 }
193 Err(RpcError::type_error("expected Float64/Float32 array"))
194 }
195 fn build_singleton(value: Self) -> Result<ArrayRef> {
196 Ok(Arc::new(Float64Array::from(vec![value])))
197 }
198}
199
200impl VgiArrow for f32 {
201 fn arrow_data_type() -> DataType {
202 DataType::Float32
203 }
204 fn describe_name() -> String {
205 "float".into()
206 }
207 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
208 if let Some(a) = arr.as_any().downcast_ref::<Float32Array>() {
209 return Ok(a.value(idx));
210 }
211 if let Some(a) = arr.as_any().downcast_ref::<Float64Array>() {
212 return Ok(a.value(idx) as f32);
213 }
214 Err(RpcError::type_error("expected Float32/Float64 array"))
215 }
216 fn build_singleton(value: Self) -> Result<ArrayRef> {
217 Ok(Arc::new(Float32Array::from(vec![value])))
218 }
219}
220
221impl VgiArrow for bool {
222 fn arrow_data_type() -> DataType {
223 DataType::Boolean
224 }
225 fn describe_name() -> String {
226 "bool".into()
227 }
228 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
229 Ok(as_array::<BooleanArray>(arr, "Boolean")?.value(idx))
230 }
231 fn build_singleton(value: Self) -> Result<ArrayRef> {
232 Ok(Arc::new(BooleanArray::from(vec![value])))
233 }
234}
235
236#[derive(Clone, Debug, PartialEq, Eq)]
248pub struct Bytes(pub Vec<u8>);
249
250impl From<Vec<u8>> for Bytes {
251 fn from(v: Vec<u8>) -> Self {
252 Self(v)
253 }
254}
255
256impl From<Bytes> for Vec<u8> {
257 fn from(b: Bytes) -> Self {
258 b.0
259 }
260}
261
262impl VgiArrow for Bytes {
263 fn arrow_data_type() -> DataType {
264 DataType::Binary
265 }
266 fn describe_name() -> String {
267 "bytes".into()
268 }
269 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
270 Ok(Bytes(
271 as_array::<BinaryArray>(arr, "Binary")?.value(idx).to_vec(),
272 ))
273 }
274 fn build_singleton(value: Self) -> Result<ArrayRef> {
275 let mut b = BinaryBuilder::new();
276 b.append_value(value.0);
277 Ok(Arc::new(b.finish()))
278 }
279}
280
281#[derive(Clone, Debug, PartialEq, Eq)]
288pub struct LargeString(pub String);
289
290impl From<String> for LargeString {
291 fn from(s: String) -> Self {
292 Self(s)
293 }
294}
295impl From<LargeString> for String {
296 fn from(s: LargeString) -> Self {
297 s.0
298 }
299}
300
301impl VgiArrow for LargeString {
302 fn arrow_data_type() -> DataType {
303 DataType::LargeUtf8
304 }
305 fn describe_name() -> String {
306 "str".into()
307 }
308 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
309 Ok(LargeString(
310 as_array::<LargeStringArray>(arr, "LargeUtf8")?
311 .value(idx)
312 .to_string(),
313 ))
314 }
315 fn build_singleton(value: Self) -> Result<ArrayRef> {
316 Ok(Arc::new(LargeStringArray::from(vec![value.0])))
317 }
318}
319
320#[derive(Clone, Debug, PartialEq, Eq)]
322pub struct LargeBytes(pub Vec<u8>);
323
324impl From<Vec<u8>> for LargeBytes {
325 fn from(v: Vec<u8>) -> Self {
326 Self(v)
327 }
328}
329impl From<LargeBytes> for Vec<u8> {
330 fn from(b: LargeBytes) -> Self {
331 b.0
332 }
333}
334
335impl VgiArrow for LargeBytes {
336 fn arrow_data_type() -> DataType {
337 DataType::LargeBinary
338 }
339 fn describe_name() -> String {
340 "bytes".into()
341 }
342 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
343 Ok(LargeBytes(
344 as_array::<LargeBinaryArray>(arr, "LargeBinary")?
345 .value(idx)
346 .to_vec(),
347 ))
348 }
349 fn build_singleton(value: Self) -> Result<ArrayRef> {
350 let len = i64::try_from(value.0.len())
351 .map_err(|_| RpcError::value_error("large_binary value exceeds i64 offsets"))?;
352 let offsets = OffsetBuffer::new(vec![0_i64, len].into());
353 let arr = LargeBinaryArray::new(offsets, Buffer::from_vec(value.0), None);
356 Ok(Arc::new(arr))
357 }
358}
359
360#[derive(Clone, Debug, PartialEq, Eq)]
368pub struct LargeBytesBuffer(pub Buffer);
369
370impl LargeBytesBuffer {
371 pub fn to_vec(&self) -> Vec<u8> {
373 self.0.as_slice().to_vec()
374 }
375}
376
377impl From<Vec<u8>> for LargeBytesBuffer {
378 fn from(value: Vec<u8>) -> Self {
379 Self(Buffer::from_vec(value))
380 }
381}
382
383impl From<Buffer> for LargeBytesBuffer {
384 fn from(value: Buffer) -> Self {
385 Self(value)
386 }
387}
388
389impl AsRef<[u8]> for LargeBytesBuffer {
390 fn as_ref(&self) -> &[u8] {
391 self.0.as_slice()
392 }
393}
394
395impl std::ops::Deref for LargeBytesBuffer {
396 type Target = [u8];
397
398 fn deref(&self) -> &Self::Target {
399 self.as_ref()
400 }
401}
402
403impl VgiArrow for LargeBytesBuffer {
404 fn arrow_data_type() -> DataType {
405 DataType::LargeBinary
406 }
407
408 fn describe_name() -> String {
409 "bytes".into()
410 }
411
412 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
413 let arr = as_array::<LargeBinaryArray>(arr, "LargeBinary")?;
414 let offsets = arr.value_offsets();
415 let start = usize::try_from(offsets[idx])
416 .map_err(|_| RpcError::value_error("large_binary offset is negative"))?;
417 let end = usize::try_from(offsets[idx + 1])
418 .map_err(|_| RpcError::value_error("large_binary offset is negative"))?;
419 Ok(Self(arr.values().slice_with_length(start, end - start)))
420 }
421
422 fn build_singleton(value: Self) -> Result<ArrayRef> {
423 let len = i64::try_from(value.0.len())
424 .map_err(|_| RpcError::value_error("large_binary value exceeds i64 offsets"))?;
425 let offsets = OffsetBuffer::new(vec![0_i64, len].into());
426 Ok(Arc::new(LargeBinaryArray::new(offsets, value.0, None)))
427 }
428}
429
430#[derive(Clone, Debug, PartialEq, Eq)]
433pub struct FixedBinary<const N: usize>(pub [u8; N]);
434
435impl<const N: usize> From<[u8; N]> for FixedBinary<N> {
436 fn from(b: [u8; N]) -> Self {
437 Self(b)
438 }
439}
440
441impl<const N: usize> VgiArrow for FixedBinary<N> {
442 fn arrow_data_type() -> DataType {
443 DataType::FixedSizeBinary(N as i32)
444 }
445 fn describe_name() -> String {
446 "bytes".into()
447 }
448 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
449 let a = as_array::<FixedSizeBinaryArray>(arr, "FixedSizeBinary")?;
450 let raw = a.value(idx);
451 if raw.len() != N {
452 return Err(RpcError::type_error(format!(
453 "FixedSizeBinary width mismatch: expected {N}, got {}",
454 raw.len()
455 )));
456 }
457 let mut out = [0u8; N];
458 out.copy_from_slice(raw);
459 Ok(FixedBinary(out))
460 }
461 fn build_singleton(value: Self) -> Result<ArrayRef> {
462 let arr = FixedSizeBinaryArray::try_from_iter([value.0.as_slice()].into_iter())
463 .map_err(RpcError::from)?;
464 Ok(Arc::new(arr))
465 }
466}
467
468#[derive(Clone, Debug, PartialEq, Eq)]
472pub struct DictString(pub String);
473
474impl From<String> for DictString {
475 fn from(s: String) -> Self {
476 Self(s)
477 }
478}
479impl From<DictString> for String {
480 fn from(s: DictString) -> Self {
481 s.0
482 }
483}
484
485impl VgiArrow for DictString {
486 fn arrow_data_type() -> DataType {
487 DataType::Dictionary(Box::new(DataType::Int16), Box::new(DataType::Utf8))
488 }
489 fn describe_name() -> String {
490 "str".into()
491 }
492 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
493 Ok(DictString(<String as VgiArrow>::read(arr, idx)?))
496 }
497 fn build_singleton(value: Self) -> Result<ArrayRef> {
498 use arrow_array::builder::StringDictionaryBuilder;
499 use arrow_array::types::Int16Type;
500 let mut b = StringDictionaryBuilder::<Int16Type>::new();
501 b.append_value(&value.0);
502 Ok(Arc::new(b.finish()))
503 }
504}
505
506use arrow_array::{
511 Date32Array, Decimal128Array, DurationMicrosecondArray, Time64MicrosecondArray,
512 TimestampMicrosecondArray,
513};
514
515const DATE32_EPOCH: chrono::NaiveDate = match chrono::NaiveDate::from_ymd_opt(1970, 1, 1) {
516 Some(d) => d,
517 None => panic!("epoch"),
518};
519
520impl VgiArrow for chrono::NaiveDate {
521 fn arrow_data_type() -> DataType {
522 DataType::Date32
523 }
524 fn describe_name() -> String {
525 "date".into()
526 }
527 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
528 let days = as_array::<Date32Array>(arr, "Date32")?.value(idx);
529 DATE32_EPOCH
530 .checked_add_signed(chrono::Duration::days(days as i64))
531 .ok_or_else(|| RpcError::value_error("date32 out of range"))
532 }
533 fn build_singleton(value: Self) -> Result<ArrayRef> {
534 let days = (value - DATE32_EPOCH).num_days() as i32;
535 Ok(Arc::new(Date32Array::from(vec![days])))
536 }
537}
538
539impl VgiArrow for chrono::NaiveDateTime {
540 fn arrow_data_type() -> DataType {
541 DataType::Timestamp(arrow_schema::TimeUnit::Microsecond, None)
542 }
543 fn describe_name() -> String {
544 "datetime".into()
545 }
546 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
547 let micros = as_array::<TimestampMicrosecondArray>(arr, "Timestamp(us)")?.value(idx);
548 chrono::DateTime::from_timestamp_micros(micros)
549 .map(|dt| dt.naive_utc())
550 .ok_or_else(|| RpcError::value_error("timestamp out of range"))
551 }
552 fn build_singleton(value: Self) -> Result<ArrayRef> {
553 let micros = value.and_utc().timestamp_micros();
554 Ok(Arc::new(TimestampMicrosecondArray::from(vec![micros])))
555 }
556}
557
558#[derive(Clone, Debug, PartialEq, Eq)]
561pub struct UtcTimestamp(pub chrono::DateTime<chrono::Utc>);
562
563impl From<chrono::DateTime<chrono::Utc>> for UtcTimestamp {
564 fn from(d: chrono::DateTime<chrono::Utc>) -> Self {
565 Self(d)
566 }
567}
568
569impl VgiArrow for UtcTimestamp {
570 fn arrow_data_type() -> DataType {
571 DataType::Timestamp(arrow_schema::TimeUnit::Microsecond, Some("UTC".into()))
572 }
573 fn describe_name() -> String {
574 "datetime".into()
575 }
576 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
577 let micros = as_array::<TimestampMicrosecondArray>(arr, "Timestamp(us, UTC)")?.value(idx);
578 chrono::DateTime::<chrono::Utc>::from_timestamp_micros(micros)
579 .map(UtcTimestamp)
580 .ok_or_else(|| RpcError::value_error("UTC timestamp out of range"))
581 }
582 fn build_singleton(value: Self) -> Result<ArrayRef> {
583 let micros = value.0.timestamp_micros();
584 let arr = TimestampMicrosecondArray::from(vec![micros]).with_timezone("UTC");
585 Ok(Arc::new(arr))
586 }
587}
588
589impl VgiArrow for chrono::NaiveTime {
590 fn arrow_data_type() -> DataType {
591 DataType::Time64(arrow_schema::TimeUnit::Microsecond)
592 }
593 fn describe_name() -> String {
594 "time".into()
595 }
596 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
597 let micros = as_array::<Time64MicrosecondArray>(arr, "Time64(us)")?.value(idx);
598 let secs = (micros / 1_000_000) as u32;
599 let nanos = ((micros % 1_000_000) * 1_000) as u32;
600 chrono::NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos)
601 .ok_or_else(|| RpcError::value_error("time-of-day out of range"))
602 }
603 fn build_singleton(value: Self) -> Result<ArrayRef> {
604 use chrono::Timelike;
605 let micros = (value.num_seconds_from_midnight() as i64) * 1_000_000
606 + (value.nanosecond() as i64) / 1_000;
607 Ok(Arc::new(Time64MicrosecondArray::from(vec![micros])))
608 }
609}
610
611impl VgiArrow for chrono::Duration {
612 fn arrow_data_type() -> DataType {
613 DataType::Duration(arrow_schema::TimeUnit::Microsecond)
614 }
615 fn describe_name() -> String {
616 "duration".into()
617 }
618 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
619 let micros = as_array::<DurationMicrosecondArray>(arr, "Duration(us)")?.value(idx);
620 Ok(chrono::Duration::microseconds(micros))
621 }
622 fn build_singleton(value: Self) -> Result<ArrayRef> {
623 let micros = value.num_microseconds().ok_or_else(|| {
624 RpcError::value_error("duration overflows microsecond representation")
625 })?;
626 Ok(Arc::new(DurationMicrosecondArray::from(vec![micros])))
627 }
628}
629
630#[derive(Clone, Copy, Debug, PartialEq, Eq)]
634pub struct Decimal20_4(pub rust_decimal::Decimal);
635
636impl From<rust_decimal::Decimal> for Decimal20_4 {
637 fn from(d: rust_decimal::Decimal) -> Self {
638 Self(d)
639 }
640}
641
642impl VgiArrow for Decimal20_4 {
643 fn arrow_data_type() -> DataType {
644 DataType::Decimal128(20, 4)
645 }
646 fn describe_name() -> String {
647 "Decimal".into()
648 }
649 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
650 let raw = as_array::<Decimal128Array>(arr, "Decimal128")?.value(idx);
651 let mut d = rust_decimal::Decimal::from_i128_with_scale(raw, 4);
653 d.normalize_assign();
654 Ok(Decimal20_4(d))
655 }
656 fn build_singleton(value: Self) -> Result<ArrayRef> {
657 let mut d = value.0;
658 d.rescale(4);
659 let raw = d.mantissa();
660 let arr = Decimal128Array::from(vec![raw])
661 .with_precision_and_scale(20, 4)
662 .map_err(RpcError::from)?;
663 Ok(Arc::new(arr))
664 }
665}
666
667impl<T> VgiArrow for Option<T>
672where
673 T: VgiArrow,
674{
675 fn arrow_data_type() -> DataType {
676 T::arrow_data_type()
677 }
678 fn nullable() -> bool {
679 true
680 }
681 fn describe_name() -> String {
682 format!("{} | None", T::describe_name())
683 }
684 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
685 if arr.is_null(idx) {
686 Ok(None)
687 } else {
688 Ok(Some(T::read(arr, idx)?))
689 }
690 }
691 fn build_singleton(value: Self) -> Result<ArrayRef> {
692 match value {
693 Some(v) => T::build_singleton(v),
694 None => build_null_singleton::<T>(),
695 }
696 }
697}
698
699fn build_null_singleton<T: VgiArrow>() -> Result<ArrayRef> {
700 use arrow_array::array::new_null_array;
701 Ok(new_null_array(&T::arrow_data_type(), 1))
702}
703
704impl<T> VgiArrow for Vec<T>
714where
715 T: VgiArrow,
716{
717 fn arrow_data_type() -> DataType {
718 DataType::List(Arc::new(Field::new("item", T::arrow_data_type(), true)))
719 }
720 fn describe_name() -> String {
721 format!("list[{}]", T::describe_name())
722 }
723 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
724 let la = as_array::<ListArray>(arr, "List")?;
725 let inner = la.value(idx);
726 let len = inner.len();
727 let mut out = Vec::with_capacity(len);
728 for i in 0..len {
729 out.push(T::read(inner.as_ref(), i)?);
730 }
731 Ok(out)
732 }
733 fn build_singleton(values: Self) -> Result<ArrayRef> {
734 let len = values.len();
740 let mut singletons: Vec<ArrayRef> = Vec::with_capacity(len);
741 for v in values {
742 singletons.push(T::build_singleton(v)?);
743 }
744 let refs: Vec<&dyn Array> = singletons.iter().map(|a| a.as_ref()).collect();
745 let inner = if refs.is_empty() {
746 arrow_array::array::new_empty_array(&T::arrow_data_type())
747 } else {
748 arrow_select::concat::concat(&refs).map_err(RpcError::from)?
749 };
750 let offsets = arrow_buffer::OffsetBuffer::new(arrow_buffer::ScalarBuffer::from(vec![
751 0i32, len as i32,
752 ]));
753 let field = Arc::new(Field::new("item", T::arrow_data_type(), true));
754 Ok(Arc::new(ListArray::new(field, offsets, inner, None)))
755 }
756}
757
758impl<V> VgiArrow for Vec<(String, V)>
769where
770 V: VgiArrow,
771{
772 fn arrow_data_type() -> DataType {
773 let entries = Field::new(
780 "entries",
781 DataType::Struct(
782 vec![
783 Field::new("key", DataType::Utf8, false),
784 Field::new("value", V::arrow_data_type(), true),
785 ]
786 .into(),
787 ),
788 false,
789 );
790 DataType::Map(Arc::new(entries), false)
791 }
792 fn describe_name() -> String {
793 format!("dict[str, {}]", V::describe_name())
794 }
795 fn read(arr: &dyn Array, idx: usize) -> Result<Self> {
796 let m = as_array::<MapArray>(arr, "Map")?;
797 let entry = m.value(idx);
798 let keys = as_array::<StringArray>(entry.column(0).as_ref(), "Map.keys (Utf8)")?;
799 let values = entry.column(1);
800 let mut out = Vec::with_capacity(keys.len());
801 for i in 0..keys.len() {
802 let v = V::read(values.as_ref(), i)?;
803 out.push((keys.value(i).to_string(), v));
804 }
805 Ok(out)
806 }
807 fn build_singleton(entries: Self) -> Result<ArrayRef> {
808 use arrow_array::array::new_empty_array;
809 let len = entries.len();
810 let (keys, values): (Vec<String>, Vec<V>) = entries.into_iter().unzip();
811 let key_arr = Arc::new(StringArray::from(keys)) as ArrayRef;
812 let value_arr: ArrayRef = if values.is_empty() {
813 new_empty_array(&V::arrow_data_type())
814 } else {
815 let mut singletons: Vec<ArrayRef> = Vec::with_capacity(values.len());
816 for v in values {
817 singletons.push(V::build_singleton(v)?);
818 }
819 let refs: Vec<&dyn Array> = singletons.iter().map(|a| a.as_ref()).collect();
820 arrow_select::concat::concat(&refs).map_err(RpcError::from)?
821 };
822 let entries_struct = arrow_array::StructArray::from(vec![
825 (Arc::new(Field::new("key", DataType::Utf8, false)), key_arr),
826 (
827 Arc::new(Field::new("value", V::arrow_data_type(), true)),
828 value_arr,
829 ),
830 ]);
831 let offsets = arrow_buffer::OffsetBuffer::new(arrow_buffer::ScalarBuffer::from(vec![
832 0i32, len as i32,
833 ]));
834 let entries_field = Arc::new(Field::new(
835 "entries",
836 entries_struct.data_type().clone(),
837 false,
838 ));
839 Ok(Arc::new(MapArray::new(
840 entries_field,
841 offsets,
842 entries_struct,
843 None,
844 false,
845 )))
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 use arrow_array::RecordBatch;
853 use arrow_schema::Schema;
854
855 fn round_trip<T: VgiArrow + std::fmt::Debug + PartialEq>(value: T) -> T {
856 let arr = T::build_singleton(value).expect("build_singleton");
857 let schema = Arc::new(Schema::new(vec![Field::new(
858 "v",
859 T::arrow_data_type(),
860 T::nullable(),
861 )]));
862 let batch = RecordBatch::try_new(schema, vec![arr]).unwrap();
863 T::read(batch.column(0).as_ref(), 0).expect("read")
864 }
865
866 #[test]
867 fn roundtrip_string() {
868 assert_eq!(round_trip("hello".to_string()), "hello".to_string());
869 }
870
871 #[test]
872 fn roundtrip_i64() {
873 assert_eq!(round_trip(42i64), 42);
874 assert_eq!(round_trip(-1i64), -1);
875 }
876
877 #[test]
878 fn roundtrip_i32() {
879 assert_eq!(round_trip(7i32), 7);
880 }
881
882 #[test]
883 fn roundtrip_f64() {
884 assert_eq!(round_trip(1.5f64), 1.5);
885 }
886
887 #[test]
888 fn roundtrip_f32() {
889 assert_eq!(round_trip(2.5f32), 2.5);
890 }
891
892 #[test]
893 fn roundtrip_bool() {
894 assert!(round_trip(true));
895 assert!(!round_trip(false));
896 }
897
898 #[test]
899 fn roundtrip_bytes() {
900 assert_eq!(
901 round_trip(Bytes(vec![1, 2, 3, 4, 5])),
902 Bytes(vec![1, 2, 3, 4, 5])
903 );
904 }
905
906 #[test]
907 fn large_bytes_build_reuses_vec_allocation() {
908 let payload = vec![1_u8; 1024];
909 let payload_ptr = payload.as_ptr();
910 let arr = LargeBytes::build_singleton(LargeBytes(payload)).unwrap();
911 let arr = arr.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
912
913 assert_eq!(arr.value(0).as_ptr(), payload_ptr);
914 }
915
916 #[test]
917 fn large_bytes_buffer_roundtrip_reuses_arrow_value_buffer() {
918 let input = LargeBinaryArray::from_iter_values([
919 b"prefix".as_slice(),
920 b"the payload remains in its Arrow buffer".as_slice(),
921 ]);
922 let input_ptr = input.value(1).as_ptr();
923
924 let value = LargeBytesBuffer::read(&input, 1).unwrap();
925 assert_eq!(value.as_ref(), input.value(1));
926 assert_eq!(value.as_ref().as_ptr(), input_ptr);
927
928 let output = LargeBytesBuffer::build_singleton(value).unwrap();
929 let output = output.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
930 assert_eq!(output.value(0), input.value(1));
931 assert_eq!(output.value(0).as_ptr(), input_ptr);
932 }
933
934 #[test]
935 fn roundtrip_option_some() {
936 assert_eq!(round_trip(Some(123i64)), Some(123));
937 }
938
939 #[test]
940 fn roundtrip_option_none() {
941 assert_eq!(round_trip::<Option<i64>>(None), None);
942 }
943
944 #[test]
945 fn roundtrip_option_string() {
946 assert_eq!(
947 round_trip(Some("hello".to_string())),
948 Some("hello".to_string())
949 );
950 assert_eq!(round_trip::<Option<String>>(None), None);
951 }
952
953 #[test]
954 fn roundtrip_vec_i64() {
955 assert_eq!(round_trip(vec![1i64, 2, 3, 4, 5]), vec![1, 2, 3, 4, 5]);
956 let empty: Vec<i64> = Vec::new();
957 assert_eq!(round_trip(empty.clone()), empty);
958 }
959
960 #[test]
961 fn roundtrip_vec_string() {
962 assert_eq!(
963 round_trip(vec!["a".to_string(), "b".to_string()]),
964 vec!["a".to_string(), "b".to_string()]
965 );
966 }
967
968 #[test]
969 fn roundtrip_vec_f64() {
970 assert_eq!(round_trip(vec![1.0f64, 2.5]), vec![1.0, 2.5]);
971 }
972
973 #[test]
974 fn roundtrip_vec_bool() {
975 assert_eq!(round_trip(vec![true, false, true]), vec![true, false, true]);
976 }
977
978 #[test]
979 fn roundtrip_vec_vec_i64() {
980 let v = vec![vec![1i64, 2], vec![3], vec![]];
981 assert_eq!(round_trip(v.clone()), v);
982 }
983
984 #[test]
985 fn roundtrip_map_str_i64() {
986 let m = vec![("a".to_string(), 1i64), ("b".into(), 2)];
987 assert_eq!(round_trip(m.clone()), m);
988 }
989
990 #[test]
991 fn roundtrip_map_str_str() {
992 let m = vec![
993 ("k1".to_string(), "v1".to_string()),
994 ("k2".into(), "v2".into()),
995 ];
996 assert_eq!(round_trip(m.clone()), m);
997 }
998
999 #[test]
1000 fn describe_names_match_python() {
1001 assert_eq!(<String as VgiArrow>::describe_name(), "str");
1002 assert_eq!(<i64 as VgiArrow>::describe_name(), "int");
1003 assert_eq!(<f64 as VgiArrow>::describe_name(), "float");
1004 assert_eq!(<bool as VgiArrow>::describe_name(), "bool");
1005 assert_eq!(<Bytes as VgiArrow>::describe_name(), "bytes");
1006 assert_eq!(<Option<String> as VgiArrow>::describe_name(), "str | None");
1007 assert_eq!(<Vec<i64> as VgiArrow>::describe_name(), "list[int]");
1008 assert_eq!(
1009 <Vec<Vec<i64>> as VgiArrow>::describe_name(),
1010 "list[list[int]]"
1011 );
1012 assert_eq!(
1013 <Vec<(String, i64)> as VgiArrow>::describe_name(),
1014 "dict[str, int]"
1015 );
1016 }
1017
1018 #[test]
1019 fn nullable_flag_only_set_for_option() {
1020 assert!(!<i64 as VgiArrow>::nullable());
1021 assert!(!<String as VgiArrow>::nullable());
1022 assert!(<Option<i64> as VgiArrow>::nullable());
1023 assert!(<Option<Vec<i64>> as VgiArrow>::nullable());
1024 }
1025}