use super::Writer;
use crate::binary::{tag, varint};
use crate::model::value::{Map, Value};
use crate::wire::binary::layout::narrows;
#[derive(Clone, Copy, PartialEq, Eq)]
enum ListForm {
Items,
Ints,
Floats,
Records,
}
impl ListForm {
fn of(items: &[Value]) -> ListForm {
match items.split_first() {
Some((Value::Int(_), rest)) if rest.iter().all(|v| matches!(v, Value::Int(_))) => {
ListForm::Ints
}
Some((Value::Float(_), rest)) if rest.iter().all(|v| matches!(v, Value::Float(_))) => {
ListForm::Floats
}
Some((Value::Map(first), rest))
if !first.is_empty()
&& rest
.iter()
.all(|v| matches!(v, Value::Map(m) if m.keys().eq(first.keys()))) =>
{
ListForm::Records
}
_ => ListForm::Items,
}
}
}
impl Writer {
pub fn value(&mut self, v: &Value) -> &mut Self {
match v {
Value::Null => self.null(),
Value::Bool(b) => self.bool(*b),
Value::Int(n) => self.int(*n),
Value::Float(f) => self.float(*f),
Value::Str(s) => self.str(s),
Value::List(items) => self.list_value(items),
Value::Map(map) => self.map_value(map),
Value::Ref(r) => {
self.out.push(tag::REF);
self.string(r.kind.as_str());
self.string(r.id.as_str());
self
}
Value::Bytes(b) => self.bytes(b),
}
}
fn list_value(&mut self, items: &[Value]) -> &mut Self {
match ListForm::of(items) {
ListForm::Ints => self.ints(items.iter().map(|v| match v {
Value::Int(n) => *n,
_ => 0,
})),
ListForm::Floats => self.float_items(items),
ListForm::Records => self.records_value(items),
ListForm::Items => {
self.list(items.len());
for item in items {
self.value(item);
}
self
}
}
}
fn float_items(&mut self, items: &[Value]) -> &mut Self {
let float = |v: &Value| match v {
Value::Float(f) => *f,
_ => 0.0,
};
let narrow = items.iter().all(|v| narrows(float(v)));
self.out
.push(if narrow { tag::FLOATS32 } else { tag::FLOATS });
varint(&mut self.out, items.len() as u64);
for v in items {
let f = float(v);
if narrow {
#[expect(
clippy::cast_possible_truncation,
reason = "every item narrows exactly, checked above"
)]
self.out.extend_from_slice(&(f as f32).to_le_bytes());
} else {
self.out.extend_from_slice(&f.to_le_bytes());
}
}
self
}
fn records_value(&mut self, items: &[Value]) -> &mut Self {
let Some(Value::Map(first)) = items.first() else {
return self.list(0);
};
self.out.push(tag::RECORDS);
varint(&mut self.out, items.len() as u64);
self.shape(first.keys());
for item in items {
if let Value::Map(map) = item {
self.values_of(map);
}
}
self
}
fn map_value(&mut self, map: &Map) -> &mut Self {
if map.is_empty() {
return self.map(0);
}
self.out.push(tag::MAP_SHAPE);
self.shape(map.keys());
self.values_of(map);
self
}
fn values_of(&mut self, map: &Map) {
for v in map.values() {
self.value(v);
}
}
}