use std::collections::BTreeMap;
pub enum Value {
Varint(u64),
Fixed64(f64),
Bytes(Vec<u8>),
List(Vec<Value>),
}
impl Value {
pub fn from_string(s: &str) -> Self {
Value::Bytes(s.as_bytes().to_vec())
}
pub fn from_u64(v: u64) -> Self {
Value::Varint(v)
}
}
fn write_varint(out: &mut Vec<u8>, mut value: u64) {
while value >= 0x80 {
out.push((value as u8 & 0x7F) | 0x80);
value >>= 7;
}
out.push(value as u8);
}
fn write_field(out: &mut Vec<u8>, field_number: u32, value: &Value) {
let tag = (field_number as u64) << 3;
match value {
Value::Varint(v) => {
write_varint(out, tag); write_varint(out, *v);
}
Value::Fixed64(f) => {
write_varint(out, tag | 1);
out.extend_from_slice(&f.to_le_bytes());
}
Value::Bytes(b) => {
write_varint(out, tag | 2);
write_varint(out, b.len() as u64);
out.extend_from_slice(b);
}
Value::List(items) => {
for item in items {
write_field(out, field_number, item);
}
}
}
}
pub fn encode(fields: &BTreeMap<u32, Value>) -> Vec<u8> {
let mut out = Vec::with_capacity(64);
for (num, val) in fields {
write_field(&mut out, *num, val);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn varint_roundtrip_zero() {
let mut out = Vec::new();
write_varint(&mut out, 0);
assert_eq!(out, vec![0]);
}
#[test]
fn varint_single_byte() {
let mut out = Vec::new();
write_varint(&mut out, 0x7f);
assert_eq!(out, vec![0x7f]);
}
#[test]
fn varint_multi_byte() {
let mut out = Vec::new();
write_varint(&mut out, 300);
assert_eq!(out, vec![0xAC, 0x02]);
}
#[test]
fn encode_string_field() {
let mut m = BTreeMap::new();
m.insert(1, Value::from_string("test"));
let out = encode(&m);
assert_eq!(out, vec![0x0A, 0x04, b't', b'e', b's', b't']);
}
#[test]
fn encode_int_field() {
let mut m = BTreeMap::new();
m.insert(2, Value::Varint(42));
let out = encode(&m);
assert_eq!(out, vec![0x10, 42]);
}
#[test]
fn fields_emitted_in_field_number_order() {
let mut m = BTreeMap::new();
m.insert(5, Value::Varint(1));
m.insert(1, Value::Varint(2));
let out = encode(&m);
assert_eq!(out, vec![0x08, 0x02, 0x28, 0x01]);
}
#[test]
fn list_repeats_field_number() {
let mut m = BTreeMap::new();
m.insert(
3,
Value::List(vec![Value::from_string("a"), Value::from_string("b")]),
);
let out = encode(&m);
assert_eq!(out, vec![0x1A, 0x01, b'a', 0x1A, 0x01, b'b']);
}
}