use crate::{Record, RecordSet, Value};
#[cfg(feature = "serde")]
use serde::Serialize;
use std::collections::HashMap;
use std::io;
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum SerializableValue {
String(String),
Int32(i32),
UInt32(u32),
Float32(f32),
Bool(bool),
UInt8(u8),
Int8(i8),
UInt16(u16),
Int16(i16),
Array(Vec<SerializableValue>),
}
impl SerializableValue {
fn from_value(value: &Value, record_set: &RecordSet) -> Result<Self, io::Error> {
match value {
Value::Int32(v) => Ok(SerializableValue::Int32(*v)),
Value::UInt32(v) => Ok(SerializableValue::UInt32(*v)),
Value::Float32(v) => Ok(SerializableValue::Float32(*v)),
Value::StringRef(v) => {
let string = record_set
.get_string(*v)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
Ok(SerializableValue::String(string.to_string()))
}
Value::Bool(v) => Ok(SerializableValue::Bool(*v)),
Value::UInt8(v) => Ok(SerializableValue::UInt8(*v)),
Value::Int8(v) => Ok(SerializableValue::Int8(*v)),
Value::UInt16(v) => Ok(SerializableValue::UInt16(*v)),
Value::Int16(v) => Ok(SerializableValue::Int16(*v)),
Value::Array(values) => {
let mut array = Vec::with_capacity(values.len());
for v in values {
array.push(Self::from_value(v, record_set)?);
}
Ok(SerializableValue::Array(array))
}
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct SerializableRecord {
#[cfg_attr(feature = "serde", serde(flatten))]
pub values: HashMap<String, SerializableValue>,
}
impl SerializableRecord {
pub fn from_record(record: &Record, record_set: &RecordSet) -> Result<Self, io::Error> {
let mut values = HashMap::new();
if let Some(schema) = record.schema() {
for (i, field) in schema.fields.iter().enumerate() {
if let Some(value) = record.get_value(i) {
let serializable_value = SerializableValue::from_value(value, record_set)?;
values.insert(field.name.clone(), serializable_value);
}
}
} else {
for (i, value) in record.values().iter().enumerate() {
let serializable_value = SerializableValue::from_value(value, record_set)?;
values.insert(format!("field_{i}"), serializable_value);
}
}
Ok(Self { values })
}
}
#[cfg(feature = "serde")]
pub fn export_to_json<W: io::Write>(record_set: &RecordSet, writer: W) -> Result<(), io::Error> {
let mut serializable_records = Vec::with_capacity(record_set.len());
for record in record_set.records() {
let serializable_record = SerializableRecord::from_record(record, record_set)?;
serializable_records.push(serializable_record);
}
serde_json::to_writer_pretty(writer, &serializable_records)
.map_err(|e| io::Error::other(e.to_string()))
}
#[cfg(feature = "csv_export")]
pub fn export_to_csv<W: io::Write>(record_set: &RecordSet, writer: W) -> Result<(), io::Error> {
use csv::WriterBuilder;
if record_set.is_empty() {
return Ok(());
}
let field_names = if let Some(schema) = record_set.schema() {
schema
.fields
.iter()
.map(|f| f.name.clone())
.collect::<Vec<_>>()
} else {
let record = record_set.get_record(0).unwrap();
(0..record.len())
.map(|i| format!("field_{i}"))
.collect::<Vec<_>>()
};
let mut csv_writer = WriterBuilder::new().has_headers(true).from_writer(writer);
csv_writer.write_record(&field_names)?;
for record in record_set.records() {
let mut row = Vec::with_capacity(field_names.len());
for (i, _) in field_names.iter().enumerate() {
if let Some(value) = record.get_value(i) {
let string_value = match value {
Value::Int32(v) => v.to_string(),
Value::UInt32(v) => v.to_string(),
Value::Float32(v) => v.to_string(),
Value::StringRef(v) => {
record_set.get_string(*v).unwrap_or_default().to_string()
}
Value::Bool(v) => v.to_string(),
Value::UInt8(v) => v.to_string(),
Value::Int8(v) => v.to_string(),
Value::UInt16(v) => v.to_string(),
Value::Int16(v) => v.to_string(),
Value::Array(values) => {
let mut array_str = String::new();
for (j, val) in values.iter().enumerate() {
if j > 0 {
array_str.push('|');
}
match val {
Value::Int32(v) => array_str.push_str(&v.to_string()),
Value::UInt32(v) => array_str.push_str(&v.to_string()),
Value::Float32(v) => array_str.push_str(&v.to_string()),
Value::StringRef(v) => {
array_str
.push_str(record_set.get_string(*v).unwrap_or_default());
}
Value::Bool(v) => array_str.push_str(&v.to_string()),
Value::UInt8(v) => array_str.push_str(&v.to_string()),
Value::Int8(v) => array_str.push_str(&v.to_string()),
Value::UInt16(v) => array_str.push_str(&v.to_string()),
Value::Int16(v) => array_str.push_str(&v.to_string()),
Value::Array(_) => array_str.push_str("<nested_array>"),
}
}
array_str
}
};
row.push(string_value);
} else {
row.push(String::new());
}
}
csv_writer.write_record(&row)?;
}
csv_writer.flush()?;
Ok(())
}