tstorage 0.1.0

Embedded time-series database
Documentation
use std::io::{Error, ErrorKind, Result, Write};
use tsz::{
    stream::{BufferedReader, BufferedWriter},
    DataPoint, Decode, Encode, StdDecoder, StdEncoder,
};

pub fn encode_points<W: Write>(writable: &mut W, data_points: &[crate::DataPoint]) -> Result<()> {
    if data_points.is_empty() {
        return Ok(());
    }

    let tsz_writer = BufferedWriter::new();
    let mut tsz_encoder = StdEncoder::new(data_points[0].timestamp as u64, tsz_writer);
    for data_point in data_points {
        tsz_encoder.encode(DataPoint::new(
            data_point.timestamp as u64,
            data_point.value,
        ));
    }
    let bytes = tsz_encoder.close();
    writable.write_all(&bytes)
}

pub fn decode_points(bytes: &[u8], n: usize) -> Result<Vec<crate::DataPoint>> {
    let r = BufferedReader::new(bytes.into());
    let mut decoder = StdDecoder::new(r);

    let mut points = Vec::new();

    loop {
        if points.len() == n {
            break;
        }

        match decoder.next() {
            Ok(dp) => {
                let timestamp: i64 = dp
                    .get_time()
                    .try_into()
                    .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
                points.push(crate::DataPoint {
                    timestamp,
                    value: dp.get_value(),
                });
            }
            Err(err) => {
                if err == tsz::decode::Error::EndOfStream {
                    break;
                } else {
                    return Err(Error::new(ErrorKind::InvalidInput, err.to_string()));
                }
            }
        };
    }

    match points.len() == n {
        true => Ok(points),
        false => Err(Error::new(
            ErrorKind::InvalidData,
            "could not read n points, metadata does not match data file",
        )),
    }
}

#[cfg(test)]
pub mod tests {
    use std::io::Write;

    use crate::{
        encode::{
            gorilla::{decode_points, encode_points},
            test::fake_file::FakeFile,
        },
        DataPoint,
    };

    #[test]
    fn test_encode_decode() {
        let buf = Vec::new();
        let mut fake_file = FakeFile::new(buf);
        let expected_points = [DataPoint {
            timestamp: 123,
            value: 1.0,
        }];
        encode_points(&mut fake_file, &expected_points).unwrap();
        fake_file.flush().unwrap();

        let actual_points = decode_points(&fake_file.buf, 1).unwrap();
        assert_eq!(actual_points, expected_points);
    }
}