1use ndarray::{Array, Ix1};
11use std::collections::HashMap;
12use std::fmt;
13
14use super::masked_array::ArrayError;
16
17#[derive(Clone, Debug)]
19pub enum FieldValue {
20 Bool(bool),
21 Int8(i8),
22 Int16(i16),
23 Int32(i32),
24 Int64(i64),
25 UInt8(u8),
26 UInt16(u16),
27 UInt32(u32),
28 UInt64(u64),
29 Float32(f32),
30 Float64(f64),
31 String(String),
32 }
34
35impl From<bool> for FieldValue {
37 fn from(value: bool) -> Self {
38 Self::Bool(value)
39 }
40}
41
42impl From<i8> for FieldValue {
43 fn from(value: i8) -> Self {
44 Self::Int8(value)
45 }
46}
47
48impl From<i16> for FieldValue {
49 fn from(value: i16) -> Self {
50 Self::Int16(value)
51 }
52}
53
54impl From<i32> for FieldValue {
55 fn from(value: i32) -> Self {
56 Self::Int32(value)
57 }
58}
59
60impl From<i64> for FieldValue {
61 fn from(value: i64) -> Self {
62 Self::Int64(value)
63 }
64}
65
66impl From<u8> for FieldValue {
67 fn from(value: u8) -> Self {
68 Self::UInt8(value)
69 }
70}
71
72impl From<u16> for FieldValue {
73 fn from(value: u16) -> Self {
74 Self::UInt16(value)
75 }
76}
77
78impl From<u32> for FieldValue {
79 fn from(value: u32) -> Self {
80 Self::UInt32(value)
81 }
82}
83
84impl From<u64> for FieldValue {
85 fn from(value: u64) -> Self {
86 Self::UInt64(value)
87 }
88}
89
90impl From<f32> for FieldValue {
91 fn from(value: f32) -> Self {
92 Self::Float32(value)
93 }
94}
95
96impl From<f64> for FieldValue {
97 fn from(value: f64) -> Self {
98 Self::Float64(value)
99 }
100}
101
102impl From<&str> for FieldValue {
103 fn from(value: &str) -> Self {
104 Self::String(value.to_string())
105 }
106}
107
108impl From<String> for FieldValue {
109 fn from(value: String) -> Self {
110 Self::String(value)
111 }
112}
113
114impl fmt::Display for FieldValue {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 match self {
117 Self::Bool(v) => write!(f, "{v}"),
118 Self::Int8(v) => write!(f, "{v}"),
119 Self::Int16(v) => write!(f, "{v}"),
120 Self::Int32(v) => write!(f, "{v}"),
121 Self::Int64(v) => write!(f, "{v}"),
122 Self::UInt8(v) => write!(f, "{v}"),
123 Self::UInt16(v) => write!(f, "{v}"),
124 Self::UInt32(v) => write!(f, "{v}"),
125 Self::UInt64(v) => write!(f, "{v}"),
126 Self::Float32(v) => write!(f, "{v}"),
127 Self::Float64(v) => write!(f, "{v}"),
128 Self::String(v) => write!(f, "\"{v}\""),
129 }
130 }
131}
132
133#[derive(Clone, Debug, Default)]
135pub struct Record {
136 fields: HashMap<String, FieldValue>,
138
139 names: Vec<String>,
141}
142
143impl Record {
144 #[must_use]
146 pub fn new() -> Self {
147 Self::default()
148 }
149
150 pub fn add_field(&mut self, name: &str, value: FieldValue) {
152 if !self.fields.contains_key(name) {
153 self.names.push(name.to_string());
154 }
155 self.fields.insert(name.to_string(), value);
156 }
157
158 #[must_use]
160 pub fn get_field(&self, name: &str) -> Option<&FieldValue> {
161 self.fields.get(name)
162 }
163
164 pub fn get_field_mut(&mut self, name: &str) -> Option<&mut FieldValue> {
166 self.fields.get_mut(name)
167 }
168
169 #[must_use]
171 pub fn num_fields(&self) -> usize {
172 self.fields.len()
173 }
174
175 #[must_use]
177 #[allow(clippy::missing_const_for_fn)]
178 pub fn names(&self) -> &[String] {
179 &self.names
180 }
181 #[must_use]
183 pub fn pprint(&self) -> String {
184 let mut result = String::new();
185
186 let max_name_len = self
187 .names
188 .iter()
189 .map(std::string::String::len)
190 .max()
191 .unwrap_or(0);
192
193 for name in &self.names {
194 if let Some(value) = self.fields.get(name) {
195 use std::fmt::Write;
196 let _ = writeln!(&mut result, "{name:<max_name_len$}: {value}");
197 }
198 }
199
200 result
201 }
202}
203
204impl fmt::Display for Record {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 write!(
207 f,
208 "({})",
209 self.names
210 .iter()
211 .filter_map(|name| self.fields.get(name).map(|v| format!("{name}: {v}")))
212 .collect::<Vec<_>>()
213 .join(", ")
214 )
215 }
216}
217
218#[derive(Clone, Debug)]
220pub struct RecordArray {
221 pub records: Vec<Record>,
223
224 pub names: Vec<String>,
226
227 pub field_titles: HashMap<String, String>,
229
230 field_indices: HashMap<String, usize>,
232
233 shape: Vec<usize>,
235
236 allow_field_attributes: bool,
238}
239
240impl RecordArray {
241 pub fn new(records: Vec<Record>) -> Result<Self, ArrayError> {
246 if records.is_empty() {
247 return Err(ArrayError::ValueError(
248 "Records cannot be empty".to_string(),
249 ));
250 }
251
252 let names = records[0].names().to_vec();
254
255 for (i, record) in records.iter().enumerate().skip(1) {
257 let record_fields = record.names();
258 if record_fields.len() != names.len() {
259 return Err(ArrayError::ValueError(format!(
260 "Record {i} has {} fields, but expected {}",
261 record_fields.len(),
262 names.len()
263 )));
264 }
265
266 for name in &names {
267 if !record_fields.contains(name) {
268 return Err(ArrayError::ValueError(format!(
269 "Record {i} is missing field '{name}'"
270 )));
271 }
272 }
273 }
274
275 let mut field_indices = HashMap::new();
277 for name in names.iter() {
278 field_indices.insert(name.clone(), 0);
279 }
280
281 let len = records.len();
283
284 Ok(Self {
285 records,
286 names,
287 field_titles: HashMap::new(),
288 field_indices,
289 shape: vec![len],
290 allow_field_attributes: true,
291 })
292 }
293
294 pub fn with_titles(
299 records: Vec<Record>,
300 titles: HashMap<String, String>,
301 ) -> Result<Self, ArrayError> {
302 let mut record_array = Self::new(records)?;
303
304 for name in titles.keys() {
306 if !record_array.field_indices.contains_key(name) {
307 return Err(ArrayError::ValueError(format!(
308 "Cannot add title for non-existent field '{name}'"
309 )));
310 }
311 }
312
313 record_array.field_titles = titles;
314 Ok(record_array)
315 }
316
317 pub const fn set_allow_field_attributes(&mut self, allow: bool) {
319 self.allow_field_attributes = allow;
320 }
321
322 #[must_use]
324 pub const fn allow_field_attributes(&self) -> bool {
325 self.allow_field_attributes
326 }
327
328 #[must_use]
330 #[allow(clippy::missing_const_for_fn)]
331 pub fn shape(&self) -> &[usize] {
332 &self.shape
333 }
334
335 pub fn field_values(&self, name: &str) -> Result<Vec<FieldValue>, ArrayError> {
337 if !self.names.contains(&name.to_string()) {
338 return Err(ArrayError::ValueError(format!(
339 "Field '{}' not found",
340 name
341 )));
342 }
343
344 let mut values = Vec::with_capacity(self.records.len());
345 for record in &self.records {
346 if let Some(value) = record.get_field(name) {
347 values.push(value.clone());
348 } else {
349 return Err(ArrayError::ValueError(format!(
350 "Field '{}' missing in some records",
351 name
352 )));
353 }
354 }
355
356 Ok(values)
357 }
358
359 #[must_use]
361 pub fn numrecords(&self) -> usize {
362 self.records.len()
363 }
364
365 #[must_use]
367 pub fn get_record(&self, index: usize) -> Option<&Record> {
368 self.records.get(index)
369 }
370
371 pub fn get_record_mut(&mut self, index: usize) -> Option<&mut Record> {
373 self.records.get_mut(index)
374 }
375
376 pub fn get_field(&self, name: &str) -> Result<Vec<FieldValue>, ArrayError> {
384 if !self.field_indices.contains_key(name) {
385 return Err(ArrayError::ValueError(format!("Field '{name}' not found")));
386 }
387
388 let values = self
389 .records
390 .iter()
391 .map(|record| {
392 record
393 .get_field(name)
394 .expect("Field should exist based on validation")
395 .clone()
396 })
397 .collect();
398
399 Ok(values)
400 }
401
402 #[allow(clippy::cast_precision_loss)]
407 pub fn field_as_f64_array(&self, name: &str) -> Result<Array<f64, Ix1>, ArrayError> {
408 let values = self.field_values(name)?;
409
410 let mut result = Array::zeros(self.records.len());
411
412 for (i, value) in values.iter().enumerate() {
413 let val = match value {
414 FieldValue::Bool(v) => {
415 if *v {
416 1.0
417 } else {
418 0.0
419 }
420 }
421 FieldValue::Int8(v) => f64::from(*v),
422 FieldValue::Int16(v) => f64::from(*v),
423 FieldValue::Int32(v) => f64::from(*v),
424 FieldValue::Int64(v) => *v as f64,
425 FieldValue::UInt8(v) => f64::from(*v),
426 FieldValue::UInt16(v) => f64::from(*v),
427 FieldValue::UInt32(v) => f64::from(*v),
428 FieldValue::UInt64(v) => *v as f64,
429 FieldValue::Float32(v) => f64::from(*v),
430 FieldValue::Float64(v) => *v,
431 FieldValue::String(_) => {
432 return Err(ArrayError::ValueError(format!(
433 "Cannot convert field '{name}' of type String to f64"
434 )))
435 }
436 };
437
438 result[i] = val;
439 }
440
441 Ok(result)
442 }
443
444 #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
449 pub fn field_as_i64_array(&self, name: &str) -> Result<Array<i64, Ix1>, ArrayError> {
450 let values = self.field_values(name)?;
451
452 let mut result = Array::zeros(self.records.len());
453
454 for (i, value) in values.iter().enumerate() {
455 let val = match value {
456 FieldValue::Bool(v) => i64::from(*v),
457 FieldValue::Int8(v) => i64::from(*v),
458 FieldValue::Int16(v) => i64::from(*v),
459 FieldValue::Int32(v) => i64::from(*v),
460 FieldValue::Int64(v) => *v,
461 FieldValue::UInt8(v) => i64::from(*v),
462 FieldValue::UInt16(v) => i64::from(*v),
463 FieldValue::UInt32(v) => i64::from(*v),
464 FieldValue::UInt64(v) => {
465 if *v > i64::MAX as u64 {
466 return Err(ArrayError::ValueError(format!(
467 "Value {v} in field '{name}' is too large for i64"
468 )));
469 }
470 *v as i64
471 }
472 FieldValue::Float32(v) => *v as i64,
473 FieldValue::Float64(v) => *v as i64,
474 FieldValue::String(_) => {
475 return Err(ArrayError::ValueError(format!(
476 "Cannot convert field '{name}' of type String to i64"
477 )))
478 }
479 };
480
481 result[i] = val;
482 }
483
484 Ok(result)
485 }
486
487 pub fn name_4(&self, name: &str) -> Result<Vec<String>, ArrayError> {
492 let values = self.field_values(name)?;
493
494 let mut result = Vec::with_capacity(self.records.len());
495
496 for value in values {
497 let val = match value {
498 FieldValue::Bool(v) => v.to_string(),
499 FieldValue::Int8(v) => v.to_string(),
500 FieldValue::Int16(v) => v.to_string(),
501 FieldValue::Int32(v) => v.to_string(),
502 FieldValue::Int64(v) => v.to_string(),
503 FieldValue::UInt8(v) => v.to_string(),
504 FieldValue::UInt16(v) => v.to_string(),
505 FieldValue::UInt32(v) => v.to_string(),
506 FieldValue::UInt64(v) => v.to_string(),
507 FieldValue::Float32(v) => v.to_string(),
508 FieldValue::Float64(v) => v.to_string(),
509 FieldValue::String(v) => v,
510 };
511
512 result.push(val);
513 }
514
515 Ok(result)
516 }
517
518 pub fn get_field_by_title(&self, title: &str) -> Result<Vec<FieldValue>, ArrayError> {
523 let name = self
525 .field_titles
526 .iter()
527 .find_map(|(name, t)| if t == title { Some(name) } else { None })
528 .ok_or_else(|| ArrayError::ValueError(format!("Title '{title}' not found")))?;
529
530 self.field_values(name)
532 }
533
534 pub fn name_5(
539 &mut self,
540 name: &str,
541 record_idx: usize,
542 value: FieldValue,
543 ) -> Result<(), ArrayError> {
544 if !self.field_indices.contains_key(name) {
546 return Err(ArrayError::ValueError(format!("Field '{name}' not found")));
547 }
548
549 let record = self.get_record_mut(record_idx).ok_or_else(|| {
551 ArrayError::ValueError(format!("Record index {record_idx} out of bounds"))
552 })?;
553
554 record.add_field(name, value);
555 Ok(())
556 }
557
558 pub fn name_6(&mut self, name: &str, values: Vec<FieldValue>) -> Result<(), ArrayError> {
563 if self.field_indices.contains_key(name) {
565 return Err(ArrayError::ValueError(format!(
566 "Field '{name}' already exists"
567 )));
568 }
569
570 if values.len() != self.records.len() {
572 return Err(ArrayError::ValueError(format!(
573 "Number of values ({}) doesn't match number of records ({})",
574 values.len(),
575 self.records.len()
576 )));
577 }
578
579 for (i, record) in self.records.iter_mut().enumerate() {
581 record.add_field(name, values[i].clone());
582 }
583
584 let new_index = self.names.len();
586 self.names.push(name.to_string());
587 self.field_indices.insert(name.to_string(), new_index);
588
589 Ok(())
590 }
591
592 pub fn name_7(&mut self, name: &str) -> Result<(), ArrayError> {
597 if !self.field_indices.contains_key(name) {
599 return Err(ArrayError::ValueError(format!("Field '{name}' not found")));
600 }
601
602 for record in &mut self.records {
604 let new_names: Vec<String> = record
606 .names
607 .iter()
608 .filter(|fieldname| *fieldname != name)
609 .cloned()
610 .collect();
611
612 record.fields.remove(name);
614
615 record.names = new_names;
617 }
618
619 let index_to_remove = self.field_indices[name];
621
622 self.names.remove(index_to_remove);
624
625 self.field_titles.remove(name);
627
628 self.field_indices.clear();
630 for (i, fieldname) in self.names.iter().enumerate() {
631 self.field_indices.insert(fieldname.clone(), i);
632 }
633
634 Ok(())
635 }
636
637 pub fn name_8(&mut self, old_name: &str, newname: &str) -> Result<(), ArrayError> {
642 if !self.field_indices.contains_key(old_name) {
644 return Err(ArrayError::ValueError(format!(
645 "Field '{old_name}' not found"
646 )));
647 }
648
649 if self.field_indices.contains_key(newname) {
651 return Err(ArrayError::ValueError(format!(
652 "Field '{newname}' already exists"
653 )));
654 }
655
656 for record in &mut self.records {
658 if let Some(value) = record.fields.remove(old_name) {
660 record.add_field(newname, value);
662
663 let old_index = record
665 .names
666 .iter()
667 .position(|name| name == old_name)
668 .expect("Failed to create RecordArray in test");
669 record.names[old_index] = newname.to_string();
670 }
671 }
672
673 let old_index = self.field_indices[old_name];
675 self.names[old_index] = newname.to_string();
676
677 self.field_indices.remove(old_name);
679 self.field_indices.insert(newname.to_string(), old_index);
680
681 if let Some(title) = self.field_titles.remove(old_name) {
683 self.field_titles.insert(newname.to_string(), title);
684 }
685
686 Ok(())
687 }
688
689 pub fn view(&self, indices: &[usize]) -> Result<Self, ArrayError> {
694 let mut newrecords = Vec::with_capacity(indices.len());
695
696 for &idx in indices {
698 if idx >= self.records.len() {
699 return Err(ArrayError::ValueError(format!(
700 "Index {idx} out of bounds for record array of length {}",
701 self.records.len()
702 )));
703 }
704
705 newrecords.push(self.records[idx].clone());
706 }
707
708 let result = Self {
710 records: newrecords,
711 names: self.names.clone(),
712 field_titles: self.field_titles.clone(),
713 field_indices: self.field_indices.clone(),
714 shape: vec![indices.len()],
715 allow_field_attributes: self.allow_field_attributes,
716 };
717
718 Ok(result)
719 }
720
721 pub fn filter<F>(&self, name: &str, condition: F) -> Result<Self, ArrayError>
726 where
727 F: Fn(&FieldValue) -> bool,
728 {
729 if !self.field_indices.contains_key(name) {
731 return Err(ArrayError::ValueError(format!("Field '{name}' not found")));
732 }
733
734 let values = self.field_values(name)?;
736
737 let mut indices = Vec::new();
739 for (i, value) in values.iter().enumerate() {
740 if condition(value) {
741 indices.push(i);
742 }
743 }
744
745 self.view(&indices)
747 }
748
749 pub fn merge(&self, other: &Self) -> Result<Self, ArrayError> {
754 if self.names.len() != other.names.len() {
756 return Err(ArrayError::ValueError(format!(
757 "Cannot merge record arrays with different number of fields ({} vs {})",
758 self.names.len(),
759 other.names.len()
760 )));
761 }
762
763 for name in &self.names {
764 if !other.field_indices.contains_key(name) {
765 return Err(ArrayError::ValueError(format!(
766 "Field '{name}' not found in the second record array"
767 )));
768 }
769 }
770
771 let mut newrecords = Vec::with_capacity(self.records.len() + other.records.len());
773 newrecords.extend_from_slice(&self.records);
774 newrecords.extend_from_slice(&other.records);
775
776 let result = Self {
778 records: newrecords,
779 names: self.names.clone(),
780 field_titles: self.field_titles.clone(),
781 field_indices: self.field_indices.clone(),
782 shape: vec![self.records.len() + other.records.len()],
783 allow_field_attributes: self.allow_field_attributes,
784 };
785
786 Ok(result)
787 }
788}
789
790#[allow(dead_code)]
792fn compare_field_values(a: &FieldValue, b: &FieldValue) -> Option<std::cmp::Ordering> {
793 match (a, b) {
794 (FieldValue::Bool(a), FieldValue::Bool(b)) => Some(a.cmp(b)),
796 (FieldValue::Int8(a), FieldValue::Int8(b)) => Some(a.cmp(b)),
797 (FieldValue::Int16(a), FieldValue::Int16(b)) => Some(a.cmp(b)),
798 (FieldValue::Int32(a), FieldValue::Int32(b)) => Some(a.cmp(b)),
799 (FieldValue::Int64(a), FieldValue::Int64(b)) => Some(a.cmp(b)),
800 (FieldValue::UInt8(a), FieldValue::UInt8(b)) => Some(a.cmp(b)),
801 (FieldValue::UInt16(a), FieldValue::UInt16(b)) => Some(a.cmp(b)),
802 (FieldValue::UInt32(a), FieldValue::UInt32(b)) => Some(a.cmp(b)),
803 (FieldValue::UInt64(a), FieldValue::UInt64(b)) => Some(a.cmp(b)),
804 (FieldValue::Float32(a), FieldValue::Float32(b)) => a.partial_cmp(b),
805 (FieldValue::Float64(a), FieldValue::Float64(b)) => a.partial_cmp(b),
806 (FieldValue::String(a), FieldValue::String(b)) => Some(a.cmp(b)),
807
808 (FieldValue::Int8(a), FieldValue::Float32(b)) => (*a as f32).partial_cmp(b),
810 (FieldValue::Int8(a), FieldValue::Float64(b)) => (*a as f64).partial_cmp(b),
811 (FieldValue::Float32(a), FieldValue::Int8(b)) => a.partial_cmp(&(*b as f32)),
812 (FieldValue::Float64(a), FieldValue::Int8(b)) => a.partial_cmp(&(*b as f64)),
813
814 _ => {
816 let type_a = std::any::type_name::<FieldValue>();
817 let type_b = std::any::type_name::<FieldValue>();
818 Some(type_a.cmp(type_b))
819 }
820 }
821}
822
823impl fmt::Display for RecordArray {
824 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
825 writeln!(f, "RecordArray(")?;
826
827 let maxrecords_to_show = 10;
828 let numrecords = self.records.len();
829 let show_all = numrecords <= maxrecords_to_show;
830
831 let records_to_show = if show_all {
832 &self.records[..]
833 } else {
834 let half = maxrecords_to_show / 2;
835 &self.records[..half]
836 };
837
838 for record in records_to_show {
839 writeln!(f, " {record},")?;
840 }
841
842 if !show_all {
843 writeln!(f, " ...")?;
844
845 let half = maxrecords_to_show / 2;
846 let remaining = &self.records[numrecords - half..];
847
848 for record in remaining {
849 writeln!(f, " {record},")?;
850 }
851 }
852
853 write!(f, ")")
854 }
855}
856
857#[allow(dead_code)]
862pub fn from_arrays(names: &[&str], arrays: &[Vec<FieldValue>]) -> Result<RecordArray, ArrayError> {
863 if names.len() != arrays.len() {
864 return Err(ArrayError::ValueError(format!(
865 "Number of field _names ({}) must match number of arrays ({})",
866 names.len(),
867 arrays.len()
868 )));
869 }
870
871 if arrays.is_empty() {
872 return Err(ArrayError::ValueError("No arrays provided".to_string()));
873 }
874
875 let numrecords = arrays[0].len();
876
877 for (i, array) in arrays.iter().enumerate().skip(1) {
879 if array.len() != numrecords {
880 return Err(ArrayError::ValueError(format!(
881 "Array {i} has length {}, but expected {numrecords}",
882 array.len()
883 )));
884 }
885 }
886
887 let mut records = Vec::with_capacity(numrecords);
889
890 for i in 0..numrecords {
891 let mut record = Record::new();
892
893 for (name, array) in names.iter().zip(arrays.iter()) {
894 record.add_field(name, array[i].clone());
895 }
896
897 records.push(record);
898 }
899
900 RecordArray::new(records)
901}
902
903#[allow(dead_code)]
908pub fn record_array_from_typed_arrays<A, B, C>(
909 names: &[&str],
910 arrays: (&[A], &[B], &[C]),
911) -> Result<RecordArray, ArrayError>
912where
913 A: Clone + Into<FieldValue>,
914 B: Clone + Into<FieldValue>,
915 C: Clone + Into<FieldValue>,
916{
917 if names.len() != 3 {
918 return Err(ArrayError::ValueError(format!(
919 "Number of field _names ({}) must match number of arrays (3)",
920 names.len()
921 )));
922 }
923
924 let a_len = arrays.0.len();
925 let b_len = arrays.1.len();
926 let c_len = arrays.2.len();
927
928 if a_len != b_len || a_len != c_len {
930 return Err(ArrayError::ValueError(format!(
931 "Arrays have different lengths: {a_len}, {b_len}, {c_len}"
932 )));
933 }
934
935 let mut records = Vec::with_capacity(a_len);
937
938 for i in 0..a_len {
939 let mut record = Record::new();
940
941 record.add_field(names[0], arrays.0[i].clone().into());
942 record.add_field(names[1], arrays.1[i].clone().into());
943 record.add_field(names[2], arrays.2[i].clone().into());
944
945 records.push(record);
946 }
947
948 RecordArray::new(records)
949}
950
951#[allow(dead_code)]
956pub fn record_array_fromrecords<A, B, C>(
957 names: &[&str],
958 tuples: &[(A, B, C)],
959) -> Result<RecordArray, ArrayError>
960where
961 A: Clone + Into<FieldValue>,
962 B: Clone + Into<FieldValue>,
963 C: Clone + Into<FieldValue>,
964{
965 if names.len() != 3 {
966 return Err(ArrayError::ValueError(format!(
967 "Number of field _names ({}) must match number of tuple elements (3)",
968 names.len()
969 )));
970 }
971
972 let mut records = Vec::with_capacity(tuples.len());
974
975 for tuple in tuples {
976 let mut record = Record::new();
977
978 record.add_field(names[0], tuple.0.clone().into());
979 record.add_field(names[1], tuple.1.clone().into());
980 record.add_field(names[2], tuple.2.clone().into());
981
982 records.push(record);
983 }
984
985 RecordArray::new(records)
986}