btlv 0.2.0

Lightning Network BOLT TLV encoding, decoding, and struct mapping
Documentation
//! Serde support for [`TlvStream`].
//!
//! A `TlvStream` serializes as a hex-encoded string of its wire-format bytes.
//! With the `serde` feature enabled, structs generated by [`tlv_struct!`] also
//! implement `Serialize`/`Deserialize` by delegating to `TlvStream`.

use crate::stream::TlvStream;
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;

impl Serialize for TlvStream {
    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
        let bytes = self.to_bytes().map_err(serde::ser::Error::custom)?;
        serializer.serialize_str(&hex::encode(bytes))
    }
}

impl<'de> Deserialize<'de> for TlvStream {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
        struct V;
        impl<'de> serde::de::Visitor<'de> for V {
            type Value = TlvStream;
            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "a hex string representing a Lightning TLV stream")
            }
            fn visit_str<E: DeError>(self, s: &str) -> std::result::Result<Self::Value, E> {
                let bytes = hex::decode(s).map_err(E::custom)?;
                TlvStream::from_bytes(&bytes).map_err(E::custom)
            }
        }
        deserializer.deserialize_str(V)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::stream::TlvRecord;

    fn rec(type_: u64, value: &[u8]) -> TlvRecord {
        TlvRecord {
            type_,
            value: value.to_vec(),
        }
    }

    #[test]
    fn json_hex_roundtrip() {
        let stream = TlvStream::from(vec![rec(1, &[0x01, 0x02]), rec(5, &[0xaa])]);

        let json = serde_json::to_string(&stream).unwrap();
        let s: String = serde_json::from_str(&json).unwrap();
        assert_eq!(s, "010201020501aa");

        let back: TlvStream = serde_json::from_str(&json).unwrap();
        assert_eq!(back.len(), 2);
        let recs: Vec<_> = back.iter().collect();
        assert_eq!(recs[0].type_, 1);
        assert_eq!(recs[0].value, vec![0x01, 0x02]);
        assert_eq!(recs[1].type_, 5);
        assert_eq!(recs[1].value, vec![0xaa]);
    }

    #[test]
    fn tu64_multi_roundtrip_json() {
        let mut s = TlvStream::default();
        s.set_tu64(42, 0);
        s.set_tu64(7, 256);

        let json = serde_json::to_string(&s).unwrap();
        let s2: TlvStream = serde_json::from_str(&json).unwrap();
        assert_eq!(s2.get_tu64(42).unwrap(), Some(0));
        assert_eq!(s2.get_tu64(7).unwrap(), Some(256));
    }
}