stigmerge_peer 0.6.2

stigmerge p2p protocol and agent components
use capnp::{
    message::{self, ReaderOptions},
    serialize,
};
use veilid_core::{BareOpaqueRecordKey, BareRecordKey, RecordKey, Timestamp};

use super::{stigmerge_capnp::peer_info, Decoder, Encoder};

#[derive(Debug, PartialEq, Clone)]
pub struct PeerInfo {
    key: RecordKey,
    pub(crate) updated_at: Timestamp,
}

impl PeerInfo {
    pub fn new(key: RecordKey) -> Self {
        Self {
            key,
            updated_at: Timestamp::now(),
        }
    }

    pub fn new_updated_at(key: RecordKey, updated_at: Timestamp) -> Self {
        Self { key, updated_at }
    }

    pub fn key(&self) -> &RecordKey {
        &self.key
    }

    pub fn updated_at(&self) -> Timestamp {
        self.updated_at
    }
}

impl Encoder for PeerInfo {
    fn encode(&self) -> super::Result<Vec<u8>> {
        let mut builder = message::Builder::new_default();
        let mut peer_info_builder = builder.get_root::<peer_info::Builder>()?;

        let mut typed_key_builder = peer_info_builder.reborrow().init_key();
        typed_key_builder.set_kind(self.key.kind().into());

        let mut key_builder = typed_key_builder.reborrow().init_key();
        let key_bytes = self.key.ref_value().ref_key();
        key_builder.set_p0(u64::from_be_bytes(key_bytes[0..8].try_into()?));
        key_builder.set_p1(u64::from_be_bytes(key_bytes[8..16].try_into()?));
        key_builder.set_p2(u64::from_be_bytes(key_bytes[16..24].try_into()?));
        key_builder.set_p3(u64::from_be_bytes(key_bytes[24..32].try_into()?));
        if let Some(secret) = self.key.value().ref_encryption_key() {
            let secret_builder = typed_key_builder.reborrow().init_secret(32);
            secret_builder.copy_from_slice(secret);
        }

        peer_info_builder.set_updated_at(self.updated_at.into());

        let message = serialize::write_message_segments_to_words(&builder);
        Ok(message)
    }
}

impl Decoder for PeerInfo {
    fn decode(buf: &[u8]) -> super::Result<Self> {
        let reader = serialize::read_message(buf, ReaderOptions::new())?;
        let peer_info_reader = reader.get_root::<peer_info::Reader>()?;
        let typed_key_reader = peer_info_reader.get_key()?;
        let key_reader = typed_key_reader.get_key()?;

        let mut key_bytes = [0u8; 32];
        key_bytes[0..8].clone_from_slice(&key_reader.get_p0().to_be_bytes()[..]);
        key_bytes[8..16].clone_from_slice(&key_reader.get_p1().to_be_bytes()[..]);
        key_bytes[16..24].clone_from_slice(&key_reader.get_p2().to_be_bytes()[..]);
        key_bytes[24..32].clone_from_slice(&key_reader.get_p3().to_be_bytes()[..]);
        let secret_key = if typed_key_reader.has_secret() {
            let secret_reader = typed_key_reader.get_secret()?;
            Some(secret_reader.into())
        } else {
            None
        };

        Ok(Self {
            key: RecordKey::new(
                typed_key_reader.get_kind().into(),
                BareRecordKey::new(BareOpaqueRecordKey::new(&key_bytes), secret_key),
            ),
            updated_at: Timestamp::new(peer_info_reader.get_updated_at()),
        })
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use veilid_core::RecordKey;

    use crate::proto::{Decoder, Encoder, PeerInfo};

    #[test]
    fn test_encode_decode() {
        let key = RecordKey::from_str("VLD0:mluySonu8GKEd2AYTY0KB8y7upLHlXwm4movvucbfoQ").unwrap();
        let peer_info = PeerInfo::new(key.clone());
        let encoded = peer_info.encode().unwrap();
        let decoded = PeerInfo::decode(&encoded).unwrap();
        assert_eq!(peer_info, decoded);
        assert_eq!(key, decoded.key);
    }
}