bsread 0.1.1

Rust implementation of the BSREAD streaming protocol
Documentation
use crate::*;
use crate::value::Value;
use crate::reader::{READER_ABF32, READER_ABF64, READER_ABI16, READER_ABI32, READER_ABI64, READER_ABOOL, READER_ABU16, READER_ABU32, READER_ABU64, READER_AF32, READER_AF64, READER_AI16, READER_AI32, READER_AI64, READER_AI8, READER_AU16, READER_AU32, READER_AU64, READER_AU8, READER_BF32, READER_BF64, READER_BI16, READER_BI32, READER_BI64, READER_BOOL, READER_BU16, READER_BU32, READER_BU64, READER_F32, READER_F64, READER_I16, READER_I32, READER_I64, READER_I8, READER_STRING, READER_U16, READER_U32, READER_U64, READER_U8};
use crate::writer::{WRITER_ABF32, WRITER_ABF64, WRITER_ABI16, WRITER_ABI32, WRITER_ABI64, WRITER_ABOOL, WRITER_ABU16, WRITER_ABU32, WRITER_ABU64, WRITER_AF32, WRITER_AF64, WRITER_AI16, WRITER_AI32, WRITER_AI64, WRITER_AI8, WRITER_AU16, WRITER_AU32, WRITER_AU64, WRITER_AU8, WRITER_BF32, WRITER_BF64, WRITER_BI16, WRITER_BI32, WRITER_BI64, WRITER_BOOL, WRITER_BU16, WRITER_BU32, WRITER_BU64, WRITER_F32, WRITER_F64, WRITER_I16, WRITER_I32, WRITER_I64, WRITER_I8, WRITER_STRING, WRITER_U16, WRITER_U32, WRITER_U64, WRITER_U8};
use std::io::Cursor;
use std::collections::HashMap;
use serde_json::Value as JsonValue;

#[derive(Debug)]
pub struct ChannelConfig {
    name: String,
    typ: String,
    shape: Option<Vec<u32>>,
    elements: usize,
    element_size: usize,
    little_endian: bool,
    compression: String,
}

impl ChannelConfig {
    pub fn get_name(&self) -> String {
        self.name.clone()
    }
    pub fn get_type(&self) -> String {
        self.typ.clone()
    }
    pub fn get_shape(&self) -> Option<Vec<u32>> {
        self.shape.clone()
    }
    pub fn get_elements(&self) -> usize {
        self.elements
    }
    pub fn get_element_size(&self) -> usize {
        self.element_size
    }
    pub fn get_size(&self) -> usize {
        self.element_size * self.elements
    }
    pub fn is_little_endian(&self) -> bool {
        self.little_endian
    }
    pub fn get_compression(&self) -> String {
        self.compression.clone()
    }

    pub fn get_metadata(&self) -> HashMap<String, JsonValue> {
        let mut metadata: HashMap<String, JsonValue> = HashMap::new();
        metadata.insert("name".to_string(), JsonValue::String(self.get_name()));
        //let shape = self.get_shape().unwrap_or(Vec::new());
        let shape = self.get_shape().unwrap_or(vec![1]);
        let shape_json = JsonValue::Array(shape.into_iter().map(|num| JsonValue::Number(num.into())).collect());
        metadata.insert("shape".to_string(),shape_json);
        metadata.insert("type".to_string(), JsonValue::String(self.get_type()));
        metadata.insert("encoding".to_string(), JsonValue::String((if self.is_little_endian(){"little"} else {"big"}).to_string()));
        if self.get_compression() != "none" {
            metadata.insert("compression".to_string(), JsonValue::String(self.get_compression()));
        }
        metadata
    }
}

pub struct ChannelScalar<T> {
    config: ChannelConfig,
    reader: fn(&mut Cursor<&Vec<u8>>) -> IOResult<T>,
    writer: fn(&mut Cursor<&mut Vec<u8>>, &T) -> IOResult<()>
}

pub struct ChannelArray<T> {
    config: ChannelConfig,
    reader: fn(&mut Cursor<&Vec<u8>>, &mut [T]) -> IOResult<()>,
    writer: fn(&mut Cursor<&mut Vec<u8>>, &[T]) -> IOResult<()>
}

pub fn get_elements(shape: &Option<Vec<u32>>) -> usize {
    let nelm = shape.clone()
        .filter(|v| !v.is_empty()) // Ensure it's not empty
        .map(|v| v.into_iter().product()) // Compute product of elements
        .unwrap_or(1); //Default to 1 if None or empty
    let elements = nelm as usize;
    elements
}

fn get_element_size(typ: &str) -> usize {
    match typ {
        "bool" => 4,
        "string" => 1,
        "int8" => 1,
        "uint8" => 1,
        "int16" => 2,
        "uint16" => 2,
        "int32" => 4,
        "uint32" => 4,
        "int64" => 8,
        "uint64" => 8,
        "float32" => 4,
        "float64" => 8,
        _ => 4,
    }
}
impl<T: Default + Clone> ChannelScalar<T> {
    pub fn new(name: String, typ: String, shape: Option<Vec<u32>>, little_endian: bool, compression: String,
               reader: fn(&mut Cursor<&Vec<u8>>) -> IOResult<T>, writer: fn(&mut Cursor<&mut Vec<u8>>, &T) -> IOResult<()>) -> Self {
        let elements = get_elements(&shape);
        let element_size = get_element_size(&typ);
        let config = ChannelConfig { name, typ, shape, elements, element_size, little_endian, compression };
        Self { config, reader, writer }
    }
}


impl<T: Default + Clone> ChannelArray<T> {
    pub fn new(name: String, typ: String, shape: Option<Vec<u32>>, little_endian: bool, compression: String,
               reader: fn(&mut Cursor<&Vec<u8>>, &mut [T]) -> IOResult<()>,  writer: fn(&mut Cursor<&mut Vec<u8>>, &[T]) -> IOResult<()>) -> Self {
        let elements = get_elements(&shape);
        let element_size = get_element_size(&typ);
        let config = ChannelConfig { name, typ, shape, elements, element_size, little_endian, compression };
        Self { config, reader, writer }
    }
}

static EMPTY_CONFIG: ChannelConfig = ChannelConfig { name: String::new(), typ: String::new(), shape: None, elements: 0, element_size: 0, little_endian: false, compression: String::new() };
pub trait ChannelTrait: Send {
    fn get_config(&self) -> &ChannelConfig {
        &EMPTY_CONFIG
    }
    fn read(&self, _: &mut Cursor<&Vec<u8>>) -> IOResult<Value> {
        Err(new_error(ErrorKind::Unsupported, "Unsupported channel type"))
    }

    fn write(&self, _: &mut Cursor<&mut Vec<u8>>, _:&Value) -> IOResult<()> {
        Err(new_error(ErrorKind::Unsupported, "Unsupported channel type"))
    }

}
/*
impl ChannelTrait for Channel<i32> {
    fn read(&self, cursor: &mut Cursor<&Vec<u8>>) -> IOResult<ChannelValue> {
        let result = (self.reader)(cursor)?;
        Ok(Value::I32(result))
    }
}
 */

macro_rules! impl_channel_scalar_trait {
    ($t:ty, $variant:ident) => {
        impl ChannelTrait for ChannelScalar<$t> {
            fn read(&self, cursor: &mut Cursor<&Vec<u8>>) -> IOResult<Value> {
                    let result = (self.reader)(cursor)?;
                    Ok(Value::$variant(result))
            }

            fn write(&self, cursor: &mut Cursor<&mut Vec<u8>>, value:&Value) -> IOResult<()> {
                if let Value::$variant(data) = value {
                    (self.writer)(cursor, data)?;
                    Ok(())
                } else {
                    Err(new_error(ErrorKind::InvalidInput, "Channel write with invalid variant"))
                }
            }

            fn get_config(&self) -> &ChannelConfig{
                return &self.config
            }
        }
    };
}


macro_rules! impl_channel_array_trait {
    ($t:ty, $variant:ident) => {
        impl ChannelTrait for ChannelArray<$t> {
           fn read(&self, cursor: &mut Cursor<&Vec<u8>>) -> IOResult<Value> {
                    let mut buffer: Vec<$t> = Vec::with_capacity(self.config.elements);
                    unsafe {
                        buffer.set_len(self.config.elements); // Initialize the buffer without default values
                    }
                    (self.reader)(cursor, & mut buffer)?;
                    Ok(Value::$variant(buffer))
            }

            fn write(&self, cursor: &mut Cursor<&mut Vec<u8>>, value:&Value) -> IOResult<()> {
                if let Value::$variant(data) = value {
                    (self.writer)(cursor, data)?;
                    Ok(())
                } else {
                    Err(new_error(ErrorKind::InvalidInput, "Channel write with invalid variant"))
                }
            }

            fn get_config(&self) -> &ChannelConfig{
                return &self.config
            }
         }
    };
}

impl_channel_scalar_trait!(String, STR);
impl_channel_scalar_trait!(bool, BOOL);
impl_channel_scalar_trait!(i8,  I8);
impl_channel_scalar_trait!(i16, I16);
impl_channel_scalar_trait!(i32, I32);
impl_channel_scalar_trait!(i64, I64);
impl_channel_scalar_trait!(u8,  U8);
impl_channel_scalar_trait!(u16, U16);
impl_channel_scalar_trait!(u32, U32);
impl_channel_scalar_trait!(u64, U64);
impl_channel_scalar_trait!(f32, F32);
impl_channel_scalar_trait!(f64, F64);

impl_channel_array_trait!(bool, ABOOL);
impl_channel_array_trait!(i8,  AI8);
impl_channel_array_trait!(i16, AI16);
impl_channel_array_trait!(i32, AI32);
impl_channel_array_trait!(i64, AI64);
impl_channel_array_trait!(u8,  AU8);
impl_channel_array_trait!(u16, AU16);
impl_channel_array_trait!(u32, AU32);
impl_channel_array_trait!(u64, AU64);
impl_channel_array_trait!(f32, AF32);
impl_channel_array_trait!(f64, AF64);

impl ChannelTrait for ChannelArray<String> {
    fn read(&self, _: &mut Cursor<&Vec<u8>>) -> IOResult<Value> {
        Err(new_error(ErrorKind::Unsupported, "String array not supported"))
    }
}

pub fn new(name: String, typ:String, shape:Option<Vec<u32>>, little_endian:bool, compression:String) -> IOResult<Box<dyn ChannelTrait>> {
    let array = shape.clone().unwrap_or(vec![]).len() > 0;
    if  array {
        match typ.as_str() {
            "bool" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, READER_ABOOL, WRITER_ABOOL))),
            //"string" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, READER_ASTRING, WRITER_ASTRING))),
            "string" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, READER_STRING, WRITER_STRING))),
            "int8" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, READER_AI8, WRITER_AI8))),
            "uint8" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, READER_AU8, WRITER_AU8))),
            "int16" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AI16 } else { READER_ABI16 }, if little_endian { WRITER_AI16 } else { WRITER_ABI16 }))),
            "uint16" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AU16 } else { READER_ABU16 }, if little_endian { WRITER_AU16 } else { WRITER_ABU16 }))),
            "int32" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AI32 } else { READER_ABI32 }, if little_endian { WRITER_AI32 } else { WRITER_ABI32 }))),
            "uint32" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AU32 } else { READER_ABU32 }, if little_endian { WRITER_AU32 } else { WRITER_ABU32 }))),
            "int64" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AI64 } else { READER_ABI64 },if little_endian { WRITER_AI64 } else { WRITER_ABI64 }))),
            "uint64" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AU64 } else { READER_ABU64 }, if little_endian { WRITER_AU64 } else { WRITER_ABU64 }))),
            "float32" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AF32 } else { READER_ABF32 }, if little_endian { WRITER_AF32 } else { WRITER_ABF32 }))),
            "float64" => Ok(Box::new(ChannelArray::new(name, typ, shape, little_endian, compression, if little_endian { READER_AF64 } else { READER_ABF64 }, if little_endian { WRITER_AF64 } else { WRITER_ABF64 }))),
            _ => Err(new_error(ErrorKind::Unsupported,"Unsupported data type"))
        }
    } else {
        match typ.as_str() {
            "bool" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, READER_BOOL, WRITER_BOOL))),
            "string" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, READER_STRING, WRITER_STRING))),
            "int8" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, READER_I8, WRITER_I8))),
            "uint8" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, READER_U8, WRITER_U8))),
            "int16" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_I16 } else { READER_BI16 }, if little_endian { WRITER_I16 } else { WRITER_BI16 }))),
            "uint16" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_U16 } else { READER_BU16 }, if little_endian { WRITER_U16 } else { WRITER_BU16 }))),
            "int32" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_I32 } else { READER_BI32 }, if little_endian { WRITER_I32 } else { WRITER_BI32 }))),
            "uint32" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_U32 } else { READER_BU32 }, if little_endian { WRITER_U32 } else { WRITER_BU32 }))),
            "int64" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_I64 } else { READER_BI64 }, if little_endian { WRITER_I64 } else { WRITER_BI64 }))),
            "uint64" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_U64 } else { READER_BU64 }, if little_endian { WRITER_U64 } else { WRITER_BU64 }))),
            "float32" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_F32 } else { READER_BF32 }, if little_endian { WRITER_F32 } else { WRITER_BF32 }))),
            "float64" => Ok(Box::new(ChannelScalar::new(name, typ, shape, little_endian, compression, if little_endian { READER_F64 } else { READER_BF64 }, if little_endian { WRITER_F64 } else { WRITER_BF64 }))),
            _ => Err(new_error(ErrorKind::Unsupported,"Unsupported data type"))
        }
    }
}
pub fn copy(channel:& Box<dyn ChannelTrait>) -> IOResult<Box<dyn ChannelTrait>> {
    let name = channel.get_config().get_name().to_string();
    let typ = channel.get_config().get_type();
    let shape = channel.get_config().get_shape();
    let little_endian   =   channel.get_config().is_little_endian();
    let compression = channel.get_config().get_compression();
    new(name, typ, shape, little_endian, compression)
}