tvl-depot/tvix/nix-compat/src/nix_daemon/ser/collections.rs
Brian Olsen b88579ade4 feat(tvix/nix-compat): Add nix serialization support
This change implements the serialization part that is needed to
implement the nix daemon protocol. Previously was add deserialization
and derivers for that and this then adds the other part of that equation
so that you can write types that can then be read using deserialization.

Change-Id: I2917de634980a93822a4f5a8ad38897b9ce16d89
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12729
Autosubmit: Brian Olsen <me@griff.name>
Reviewed-by: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
2024-11-04 20:02:54 +00:00

94 lines
2.7 KiB
Rust

use std::collections::BTreeMap;
use std::future::Future;
use super::{NixSerialize, NixWrite};
impl<T> NixSerialize for Vec<T>
where
T: NixSerialize + Send + Sync,
{
#[allow(clippy::manual_async_fn)]
fn serialize<W>(&self, writer: &mut W) -> impl Future<Output = Result<(), W::Error>> + Send
where
W: NixWrite,
{
async move {
writer.write_value(&self.len()).await?;
for value in self.iter() {
writer.write_value(value).await?;
}
Ok(())
}
}
}
impl<K, V> NixSerialize for BTreeMap<K, V>
where
K: NixSerialize + Ord + Send + Sync,
V: NixSerialize + Send + Sync,
{
#[allow(clippy::manual_async_fn)]
fn serialize<W>(&self, writer: &mut W) -> impl Future<Output = Result<(), W::Error>> + Send
where
W: NixWrite,
{
async move {
writer.write_value(&self.len()).await?;
for (key, value) in self.iter() {
writer.write_value(key).await?;
writer.write_value(value).await?;
}
Ok(())
}
}
}
#[cfg(test)]
mod test {
use std::collections::BTreeMap;
use std::fmt;
use hex_literal::hex;
use rstest::rstest;
use tokio::io::AsyncWriteExt as _;
use tokio_test::io::Builder;
use crate::nix_daemon::ser::{NixSerialize, NixWrite, NixWriter};
#[rstest]
#[case::empty(vec![], &hex!("0000 0000 0000 0000"))]
#[case::one(vec![0x29], &hex!("0100 0000 0000 0000 2900 0000 0000 0000"))]
#[case::two(vec![0x7469, 10], &hex!("0200 0000 0000 0000 6974 0000 0000 0000 0A00 0000 0000 0000"))]
#[tokio::test]
async fn test_write_small_vec(#[case] value: Vec<usize>, #[case] data: &[u8]) {
let mock = Builder::new().write(data).build();
let mut writer = NixWriter::new(mock);
writer.write_value(&value).await.unwrap();
writer.flush().await.unwrap();
}
fn empty_map() -> BTreeMap<usize, u64> {
BTreeMap::new()
}
macro_rules! map {
($($key:expr => $value:expr),*) => {{
let mut ret = BTreeMap::new();
$(ret.insert($key, $value);)*
ret
}};
}
#[rstest]
#[case::empty(empty_map(), &hex!("0000 0000 0000 0000"))]
#[case::one(map![0x7469usize => 10u64], &hex!("0100 0000 0000 0000 6974 0000 0000 0000 0A00 0000 0000 0000"))]
#[tokio::test]
async fn test_write_small_btree_map<E>(#[case] value: E, #[case] data: &[u8])
where
E: NixSerialize + Send + PartialEq + fmt::Debug,
{
let mock = Builder::new().write(data).build();
let mut writer = NixWriter::new(mock);
writer.write_value(&value).await.unwrap();
writer.flush().await.unwrap();
}
}