#![allow(dead_code)]
use std::collections::HashMap;
use crate::types::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SlotId(pub u32);
impl SlotId {
#[inline]
pub fn from_usize(i: usize) -> Self {
Self(u32::try_from(i).expect("SlotId overflow"))
}
#[inline]
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Record {
values: Vec<Value>,
}
impl Record {
pub fn new() -> Self {
Self { values: Vec::new() }
}
pub fn with_capacity(n: usize) -> Self {
Self {
values: vec![Value::Null; n],
}
}
#[inline]
pub fn get(&self, slot: SlotId) -> &Value {
&self.values[slot.index()]
}
#[inline]
pub fn set(&mut self, slot: SlotId, v: Value) {
self.values[slot.index()] = v;
}
#[inline]
pub fn len(&self) -> usize {
self.values.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn push(&mut self, v: Value) -> SlotId {
let slot = SlotId::from_usize(self.values.len());
self.values.push(v);
slot
}
pub fn iter(&self) -> std::slice::Iter<'_, Value> {
self.values.iter()
}
}
impl Default for Record {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct RecordSchema {
by_name: HashMap<String, SlotId>,
names: Vec<String>,
}
impl RecordSchema {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(n: usize) -> Self {
Self {
by_name: HashMap::with_capacity(n),
names: Vec::with_capacity(n),
}
}
pub fn slot(&self, name: &str) -> Option<SlotId> {
self.by_name.get(name).copied()
}
pub fn name(&self, slot: SlotId) -> &str {
&self.names[slot.index()]
}
pub fn len(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (SlotId, &str)> {
self.names
.iter()
.enumerate()
.map(|(i, n)| (SlotId::from_usize(i), n.as_str()))
}
pub fn add(&mut self, name: impl Into<String>) -> SlotId {
let name = name.into();
if let Some(&slot) = self.by_name.get(&name) {
return slot;
}
let slot = SlotId::from_usize(self.names.len());
self.by_name.insert(name.clone(), slot);
self.names.push(name);
slot
}
pub fn names(&self) -> &[String] {
&self.names
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slot_roundtrip() {
let s = SlotId::from_usize(42);
assert_eq!(s.0, 42);
assert_eq!(s.index(), 42);
}
#[test]
fn record_with_capacity_initializes_to_null() {
let r = Record::with_capacity(3);
assert_eq!(r.len(), 3);
for v in r.iter() {
assert_eq!(v, &Value::Null);
}
}
#[test]
fn record_get_set() {
let mut r = Record::with_capacity(2);
let s0 = SlotId(0);
let s1 = SlotId(1);
r.set(s0, Value::I64(7));
r.set(s1, Value::String("hi".into()));
assert_eq!(r.get(s0), &Value::I64(7));
assert_eq!(r.get(s1), &Value::String("hi".into()));
}
#[test]
fn record_push_returns_new_slot() {
let mut r = Record::new();
assert!(r.is_empty());
let s = r.push(Value::I64(1));
assert_eq!(s, SlotId(0));
let s2 = r.push(Value::I64(2));
assert_eq!(s2, SlotId(1));
assert_eq!(r.len(), 2);
}
#[test]
fn schema_add_assigns_contiguous_slots() {
let mut sc = RecordSchema::new();
let a = sc.add("p");
let b = sc.add("p.__id");
let c = sc.add("p.name");
assert_eq!(a, SlotId(0));
assert_eq!(b, SlotId(1));
assert_eq!(c, SlotId(2));
assert_eq!(sc.len(), 3);
}
#[test]
fn schema_add_duplicate_returns_existing_slot() {
let mut sc = RecordSchema::new();
let a = sc.add("p");
let a2 = sc.add("p");
assert_eq!(a, a2);
assert_eq!(sc.len(), 1);
}
#[test]
fn schema_slot_and_name_are_inverses() {
let mut sc = RecordSchema::new();
sc.add("a");
sc.add("b.c");
sc.add("_anon_0");
for (slot, name) in sc
.iter()
.map(|(s, n)| (s, n.to_string()))
.collect::<Vec<_>>()
{
assert_eq!(sc.slot(&name), Some(slot));
assert_eq!(sc.name(slot), &name);
}
}
#[test]
fn schema_unknown_name_returns_none() {
let sc = RecordSchema::new();
assert!(sc.slot("missing").is_none());
}
#[test]
fn schema_iter_in_slot_order() {
let mut sc = RecordSchema::new();
sc.add("z");
sc.add("a");
sc.add("m");
let names: Vec<&str> = sc.iter().map(|(_, n)| n).collect();
assert_eq!(names, vec!["z", "a", "m"]);
}
}