use byteorder::{LittleEndian, WriteBytesExt};
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use crate::error::{IoError, Result};
use super::encoding::encode_column;
use super::types::{ColumnarTable, EncodingType, COLUMNAR_MAGIC, FORMAT_VERSION};
#[derive(Debug, Clone, Default)]
pub struct ColumnarWriteOptions {
pub encoding: Option<EncodingType>,
}
pub fn write_columnar<P: AsRef<Path>>(path: P, table: &ColumnarTable) -> Result<()> {
write_columnar_with_options(path, table, ColumnarWriteOptions::default())
}
pub fn write_columnar_with_options<P: AsRef<Path>>(
path: P,
table: &ColumnarTable,
options: ColumnarWriteOptions,
) -> Result<()> {
let file = File::create(path).map_err(|e| IoError::FileError(e.to_string()))?;
let mut writer = BufWriter::new(file);
writer
.write_all(COLUMNAR_MAGIC)
.map_err(|e| IoError::FileError(format!("Failed to write magic: {}", e)))?;
writer
.write_u32::<LittleEndian>(FORMAT_VERSION)
.map_err(|e| IoError::FileError(format!("Failed to write version: {}", e)))?;
writer
.write_u32::<LittleEndian>(table.num_columns() as u32)
.map_err(|e| IoError::FileError(format!("Failed to write column count: {}", e)))?;
writer
.write_u64::<LittleEndian>(table.num_rows() as u64)
.map_err(|e| IoError::FileError(format!("Failed to write row count: {}", e)))?;
for col in table.columns() {
let name_bytes = col.name.as_bytes();
writer
.write_u32::<LittleEndian>(name_bytes.len() as u32)
.map_err(|e| {
IoError::FileError(format!("Failed to write column name length: {}", e))
})?;
writer
.write_all(name_bytes)
.map_err(|e| IoError::FileError(format!("Failed to write column name: {}", e)))?;
writer
.write_u8(col.data.type_tag() as u8)
.map_err(|e| IoError::FileError(format!("Failed to write type tag: {}", e)))?;
let encoding = options.encoding.unwrap_or_else(|| col.data.best_encoding());
writer
.write_u8(encoding as u8)
.map_err(|e| IoError::FileError(format!("Failed to write encoding type: {}", e)))?;
let mut data_buf = Vec::new();
encode_column(&mut data_buf, &col.data, encoding)?;
writer
.write_u64::<LittleEndian>(data_buf.len() as u64)
.map_err(|e| IoError::FileError(format!("Failed to write data size: {}", e)))?;
writer
.write_all(&data_buf)
.map_err(|e| IoError::FileError(format!("Failed to write column data: {}", e)))?;
}
writer
.flush()
.map_err(|e| IoError::FileError(format!("Failed to flush writer: {}", e)))?;
Ok(())
}