use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
use minarrow::{SuperTable, Table};
use crate::models::encoders::json::{JsonEncodeOptions, encode_supertable_json, encode_table_json};
pub struct JsonWriter<W: Write> {
writer: W,
options: JsonEncodeOptions,
}
impl JsonWriter<Vec<u8>> {
pub fn new_vec() -> Self {
Self::new(Vec::new(), JsonEncodeOptions::default())
}
}
impl<W: Write + IntoInner> JsonWriter<W> {
pub fn into_inner(self) -> W::Inner {
self.writer.into_inner_buf()
}
}
impl<W: Write> JsonWriter<W> {
pub fn new(writer: W, options: JsonEncodeOptions) -> Self {
JsonWriter { writer, options }
}
pub fn write_table(&mut self, table: &Table) -> io::Result<()> {
encode_table_json(table, &mut self.writer, &self.options)
}
pub fn write_supertable(&mut self, st: &SuperTable) -> io::Result<()> {
encode_supertable_json(st, &mut self.writer, &self.options)
}
pub fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
impl JsonWriter<File> {
pub fn to_path<P: AsRef<Path>>(path: P, options: JsonEncodeOptions) -> io::Result<Self> {
let file = File::create(path)?;
Ok(Self::new(file, options))
}
}
pub trait IntoInner {
type Inner;
fn into_inner_buf(self) -> Self::Inner;
}
impl IntoInner for Vec<u8> {
type Inner = Vec<u8>;
fn into_inner_buf(self) -> Vec<u8> {
self
}
}
impl IntoInner for minarrow::Vec64<u8> {
type Inner = minarrow::Vec64<u8>;
fn into_inner_buf(self) -> minarrow::Vec64<u8> {
self
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::models::encoders::json::JsonFormat;
use minarrow::{
Array, ArrowType, Buffer, Field, FieldArray, IntegerArray, NumericArray, Table, Vec64,
};
use simd_json::prelude::ValueAsArray;
fn tiny_table() -> Table {
let col = FieldArray {
field: Field::new("n", ArrowType::Int32, false, None).into(),
array: Array::NumericArray(NumericArray::Int32(Arc::new(IntegerArray {
data: Buffer::from(Vec64::<i32>::from_slice(&[1, 2, 3])),
null_mask: None,
}))),
null_count: 0,
};
Table::new("t".to_string(), Some(vec![col]))
}
fn parse(bytes: Vec<u8>) -> simd_json::OwnedValue {
let mut buf = bytes;
simd_json::to_owned_value(&mut buf).unwrap()
}
#[test]
fn writer_vec_roundtrip() {
let table = tiny_table();
let mut w = JsonWriter::new_vec();
w.write_table(&table).unwrap();
let v = parse(w.into_inner());
assert_eq!(v.as_array().unwrap().len(), 3);
}
#[test]
fn writer_vec64_roundtrip_and_alignment() {
let table = tiny_table();
let mut w = JsonWriter::new(Vec64::<u8>::new(), JsonEncodeOptions::default());
w.write_table(&table).unwrap();
let bytes = w.into_inner();
assert_eq!(bytes.as_ptr() as usize % 64, 0);
let v = parse(bytes.0.into_iter().collect());
assert_eq!(v.as_array().unwrap().len(), 3);
}
#[test]
fn writer_ndjson_roundtrip() {
let table = tiny_table();
let opts = JsonEncodeOptions {
format: JsonFormat::Ndjson,
..Default::default()
};
let mut w = JsonWriter::new(Vec::new(), opts);
w.write_table(&table).unwrap();
let s = String::from_utf8(w.into_inner()).unwrap();
assert_eq!(s.lines().count(), 3);
}
#[test]
fn writer_to_path() {
let table = tiny_table();
let tmp = tempfile::NamedTempFile::new().unwrap();
{
let mut w = JsonWriter::to_path(tmp.path(), JsonEncodeOptions::default()).unwrap();
w.write_table(&table).unwrap();
w.flush().unwrap();
}
let contents = std::fs::read(tmp.path()).unwrap();
let v = parse(contents);
assert_eq!(v.as_array().unwrap().len(), 3);
}
}