clickhouse-format 0.3.0

ClickHouse Formats
Documentation
use serde::Serialize;
use serde_json::{ser::CompactFormatter, Serializer};

use crate::{format_name::FormatName, input::Input};

pub struct JsonCompactEachRowInput<T> {
    rows: Vec<Vec<T>>,
}
impl<T> JsonCompactEachRowInput<T> {
    pub fn new(rows: Vec<Vec<T>>) -> Self {
        Self { rows }
    }
}

impl<T> Input for JsonCompactEachRowInput<T>
where
    T: Serialize,
{
    type Error = serde_json::Error;

    fn format_name() -> FormatName {
        FormatName::JsonCompactEachRow
    }

    fn serialize(&self) -> Result<Vec<u8>, Self::Error> {
        let mut buf = vec![];
        let mut ser_buf = Vec::with_capacity(128);

        for row in &self.rows {
            buf.push(b'[');
            for (i, item) in row.iter().enumerate() {
                ser_buf.clear();
                let mut ser = Serializer::with_formatter(&mut ser_buf, CompactFormatter);
                item.serialize(&mut ser)?;

                buf.extend_from_slice(ser.into_inner());

                if i < (row.len() - 1) {
                    buf.extend_from_slice(b", ");
                }
            }
            buf.push(b']');

            buf.push(b'\n');
        }

        Ok(buf)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::{fs, path::PathBuf};

    use crate::test_helpers::{TEST_ROW_1, TEST_ROW_2};

    use serde_json::{Map, Value};

    #[test]
    fn simple() -> Result<(), Box<dyn std::error::Error>> {
        let file_path = PathBuf::new().join("tests/files/JSONCompactEachRow.txt");
        let content = fs::read_to_string(&file_path)?;

        assert_eq!(
            JsonCompactEachRowInput::<()>::format_name(),
            file_path
                .file_stem()
                .unwrap()
                .to_string_lossy()
                .parse()
                .unwrap()
        );

        let mut rows: Vec<Vec<Value>> = vec![];
        rows.push(vec![
            TEST_ROW_1.array1.to_owned().into(),
            TEST_ROW_1.array2.to_owned().into(),
            vec![
                Value::Number(TEST_ROW_1.tuple1.to_owned().0.into()),
                Value::String(TEST_ROW_1.tuple1.to_owned().1),
            ]
            .into(),
            vec![
                Value::Number(TEST_ROW_1.tuple2.to_owned().0.into()),
                Value::Null,
            ]
            .into(),
            Value::Object(TEST_ROW_1.map1.iter().fold(Map::new(), |mut m, (k, v)| {
                m.insert(k.to_owned(), Value::String(v.to_owned()));
                m
            })),
        ]);
        rows.push(vec![
            TEST_ROW_2.array1.to_owned().into(),
            TEST_ROW_2.array2.to_owned().into(),
            vec![
                Value::Number(TEST_ROW_2.tuple1.to_owned().0.into()),
                Value::String(TEST_ROW_2.tuple1.to_owned().1),
            ]
            .into(),
            vec![
                Value::Number(TEST_ROW_2.tuple2.to_owned().0.into()),
                Value::String(TEST_ROW_2.tuple2.to_owned().1.unwrap()),
            ]
            .into(),
            Value::Object(TEST_ROW_2.map1.iter().fold(Map::new(), |mut m, (k, v)| {
                m.insert(k.to_owned(), Value::String(v.to_owned()));
                m
            })),
        ]);

        let bytes = JsonCompactEachRowInput::new(rows).serialize()?;
        assert_eq!(bytes, content.as_bytes());

        Ok(())
    }
}