fdb_tuple/
lib.rs

1//! Implements [FoundationDB tuples](https://github.com/apple/foundationdb/blob/main/design/tuple.md)
2//!
3//! # Features
4//!
5//! - `uuid`: enables `ToBytes` and `FromBytes` for `uuid::Uuid`
6
7use from_bytes::{FromBytes, Error};
8use to_bytes::ToBytes;
9
10pub mod to_bytes;
11pub mod from_bytes;
12
13/// Converts a value to a tuple-encoded byte vector.
14///
15/// # Examples
16///
17/// ```rust
18/// use fdb_tuple::{to_bytes, from_bytes};
19///
20/// let bytes = to_bytes(("a", "b"));
21/// assert_eq!(bytes, b"\x02a\x00\x02b\x00");
22///
23/// let tuple: (String, String) = from_bytes(&bytes).unwrap();
24/// assert_eq!(tuple, ("a".to_string(), "b".to_string()));
25/// ```
26pub fn to_bytes<T: ToBytes>(value: T) -> Vec<u8> {
27    value.to_bytes(0)
28}
29
30/// Converts a byte vector to a value.
31///
32/// # Examples
33///
34/// ```rust
35/// use fdb_tuple::{to_bytes, from_bytes};
36///
37/// let bytes = to_bytes(("a", "b"));
38/// assert_eq!(bytes, b"\x02a\x00\x02b\x00");
39///
40/// let tuple: (String, String) = from_bytes(&bytes).unwrap();
41/// assert_eq!(tuple, ("a".to_string(), "b".to_string()));
42/// ```
43pub fn from_bytes<T>(bytes: &[u8]) -> Result<T, Error>
44where
45    T: FromBytes,
46{
47    let (res, rest) = T::from_bytes(bytes, 0)?;
48    if !rest.is_empty() {
49        return Err(Error { kind: from_bytes::ErrorKind::ExtraData })
50    }
51    Ok(res)
52}
53
54#[cfg(test)]
55mod test {
56    use crate::to_bytes;
57    use crate::from_bytes;
58
59    #[test]
60    fn simple_unicode_string() {
61        assert_eq!(to_bytes("foobar"), b"\x02foobar\x00");
62        assert_eq!(from_bytes::<String>(b"\x02foobar\x00").unwrap(), "foobar".to_string());
63    }
64
65    #[test]
66    fn simple_tuple() {
67        assert_eq!(to_bytes(("a", "b")), b"\x02a\x00\x02b\x00");
68        assert_eq!(from_bytes::<(String, String)>(b"\x02a\x00\x02b\x00").unwrap(), ("a".to_string(), "b".to_string()));
69    }
70
71    #[test]
72    fn nested_tuple() {
73        assert_eq!(
74            to_bytes((("a", "b"), ("c", "d"))),
75            b"\x05\x02a\x00\x02b\x00\x00\x05\x02c\x00\x02d\x00\x00"
76        );
77        assert_eq!(
78            from_bytes::<((String, String), (String, String))>(b"\x05\x02a\x00\x02b\x00\x00\x05\x02c\x00\x02d\x00\x00").unwrap(),
79            (("a".to_string(), "b".to_string()), ("c".to_string(), "d".to_string()))
80        );
81    }
82
83    #[test]
84    fn negative_i8() {
85        assert_eq!(to_bytes(-1i8), b"\x13\xfe");
86        assert_eq!(to_bytes(-128i8), b"\x13\x7f");
87    }
88
89    #[test]
90    fn number_spec() {
91        assert_eq!(to_bytes(-5551212), b"\x11\xabK\x93");
92    }
93
94    #[test]
95    fn float_spec() {
96        assert_eq!(to_bytes(-42f32), b"\x20\x3d\xd7\xff\xff");
97    }
98
99    #[test]
100    fn decode() {
101        let bytes = b"\x02cpki\x00\x18\\\xe2\xcc\xc2\x01\xe3\xd2!\x8c|\xc4x\xe7\xfd\x14\xfe\xd1.\x01u\xf9\x156\xdc\xb7\xc0_O\xc3\xc9\r\x99\xb6\xf3\xd1\xef\x99\x00\x15\t\x01\xfc0\x8e\x83\x1c\xbad\xf5Vf\x9b:2\x85\x91\xb6H!2>\x1dRL\x8b>\x00\xff\x00\xff\xad\x1d2P\xac\x00";
102        let (tag, timestamp, pubkey, kind, id): (String, u64, Vec<u8>, u64, Vec<u8>) = from_bytes(bytes).unwrap();
103        assert_eq!(tag, "cpki");
104    }
105}