matdb 0.1.0

An experimental embedded SQL-like DBMS
Documentation
use std::io::{Cursor, Write};

use anyhow::{bail, Context, Result};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

use super::Value;

const NULL_ID: u8 = 0x01;
const BOOL_FALSE_ID: u8 = 0x02;
const BOOL_TRUE_ID: u8 = 0x03;
const INT64_ID: u8 = 0x04;
const STR_ID: u8 = 0x05;

pub fn d_encode_row(values: &[Value]) -> Vec<u8> {
    let mut bytes = Vec::new();

    for value in values {
        match value {
            Value::Null => bytes.write_u8(NULL_ID).unwrap(),
            Value::Bool(b) => bytes
                .write_u8(if *b { BOOL_TRUE_ID } else { BOOL_FALSE_ID })
                .unwrap(),
            Value::Int(i) => {
                bytes.write_u8(INT64_ID).unwrap();
                bytes.write_i64::<BigEndian>(*i).unwrap();
            }
            Value::String(s) => {
                bytes.write_u8(STR_ID).unwrap();
                bytes.write(&s.as_bytes()).unwrap();
                bytes.write_u8(0).unwrap();
            }
        }
    }

    bytes
}

pub fn d_decode_row(bytes: &[u8]) -> Result<Vec<Value>> {
    let mut c = Cursor::new(bytes);
    let mut values = Vec::new();

    while let Ok(b) = c.read_u8() {
        values.push(match b {
            NULL_ID => Value::Null,
            BOOL_FALSE_ID => Value::Bool(false),
            BOOL_TRUE_ID => Value::Bool(true),
            INT64_ID => Value::Int(c.read_i64::<BigEndian>().context("failed to decode INT")?),
            STR_ID => {
                let mut v = Vec::new();
                loop {
                    let b = c.read_u8().context("improperly terminated TEXT")?;
                    if b != 0 {
                        v.push(b);
                    } else {
                        break;
                    }
                }

                Value::String(String::from_utf8(v).context("failed to decode TEXT as utf8")?)
            }
            _ => bail!("invalid value type byte {b:x?}"),
        })
    }

    Ok(values)
}