1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
use std::{borrow::Cow, convert::TryInto};

use serde::{Deserialize, Serialize};

/// A document's entry in a View's mappings.
#[derive(PartialEq, Debug)]
pub struct Map<K: Key = (), V: Serialize = ()> {
    /// The id of the document that emitted this entry.
    pub source: u64,

    /// The key used to index the View.
    pub key: K,

    /// An associated value stored in the view.
    pub value: V,
}

impl<K: Key, V: Serialize> Map<K, V> {
    /// Creates a new Map entry for the document with id `source`.
    pub fn new(source: u64, key: K, value: V) -> Self {
        Self { source, key, value }
    }
}

/// Represents a document's entry in a View's mappings, serialized and ready to store.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Serialized {
    /// The id of the document that emitted this entry.
    pub source: u64,

    /// The key used to index the View.
    pub key: Vec<u8>,

    /// An associated value stored in the view.
    pub value: Vec<u8>,
}

/// A trait that enables a type to convert itself to a big-endian/network byte order.
pub trait Key: Clone + Send + Sync {
    /// Convert `self` into an `IVec` containing bytes ordered in big-endian/network byte order.
    fn as_big_endian_bytes(&self) -> anyhow::Result<Cow<'_, [u8]>>;

    /// Convert a slice of bytes into `Self` by interpretting `bytes` in big-endian/network byte order.
    fn from_big_endian_bytes(bytes: &[u8]) -> anyhow::Result<Self>;
}

impl<'k> Key for Cow<'k, [u8]> {
    fn as_big_endian_bytes(&self) -> anyhow::Result<Cow<'k, [u8]>> {
        Ok(self.clone())
    }

    fn from_big_endian_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
        Ok(Cow::Owned(bytes.to_vec()))
    }
}

impl Key for String {
    fn as_big_endian_bytes(&self) -> anyhow::Result<Cow<'_, [u8]>> {
        Ok(Cow::Borrowed(self.as_bytes()))
    }

    fn from_big_endian_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
        Ok(Self::from_utf8(bytes.to_vec())?)
    }
}

impl Key for () {
    fn as_big_endian_bytes(&self) -> anyhow::Result<Cow<'_, [u8]>> {
        Ok(Cow::default())
    }

    fn from_big_endian_bytes(_: &[u8]) -> anyhow::Result<Self> {
        Ok(())
    }
}

#[cfg(feature = "uuid")]
impl<'k> Key for uuid::Uuid {
    fn as_big_endian_bytes(&self) -> anyhow::Result<Cow<'_, [u8]>> {
        Ok(Cow::Borrowed(self.as_bytes()))
    }

    fn from_big_endian_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
        Ok(Self::from_bytes(bytes.try_into()?))
    }
}

impl<T> Key for Option<T>
where
    T: Key,
{
    /// # Panics
    ///
    /// Panics if `T::into_big_endian_bytes` returns an empty `IVec`.
    // TODO consider removing this panic limitation by adding a single byte to
    // each key (at the end preferrably) so that we can distinguish between None
    // and a 0-byte type
    fn as_big_endian_bytes(&self) -> anyhow::Result<Cow<'_, [u8]>> {
        if let Some(contents) = self {
            let contents = contents.as_big_endian_bytes()?;
            assert!(!contents.is_empty());
            Ok(contents)
        } else {
            Ok(Cow::default())
        }
    }

    fn from_big_endian_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
        if bytes.is_empty() {
            Ok(None)
        } else {
            Ok(Some(T::from_big_endian_bytes(bytes)?))
        }
    }
}

macro_rules! impl_key_for_primitive {
    ($type:ident) => {
        impl Key for $type {
            fn as_big_endian_bytes(&self) -> anyhow::Result<Cow<'_, [u8]>> {
                Ok(Cow::from(self.to_be_bytes().to_vec()))
            }

            fn from_big_endian_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
                Ok($type::from_be_bytes(bytes.try_into()?))
            }
        }
    };
}

impl_key_for_primitive!(i8);
impl_key_for_primitive!(u8);
impl_key_for_primitive!(i16);
impl_key_for_primitive!(u16);
impl_key_for_primitive!(i32);
impl_key_for_primitive!(u32);
impl_key_for_primitive!(i64);
impl_key_for_primitive!(u64);
impl_key_for_primitive!(i128);
impl_key_for_primitive!(u128);

#[test]
#[allow(clippy::cognitive_complexity)] // I disagree - @ecton
fn primitive_key_encoding_tests() -> anyhow::Result<()> {
    macro_rules! test_primitive_extremes {
        ($type:ident) => {
            assert_eq!(
                &$type::MAX.to_be_bytes(),
                $type::MAX.as_big_endian_bytes()?.as_ref()
            );
            assert_eq!(
                $type::MAX,
                $type::from_big_endian_bytes(&$type::MAX.as_big_endian_bytes()?)?
            );
            assert_eq!(
                $type::MIN,
                $type::from_big_endian_bytes(&$type::MIN.as_big_endian_bytes()?)?
            );
        };
    }

    test_primitive_extremes!(i8);
    test_primitive_extremes!(u8);
    test_primitive_extremes!(i16);
    test_primitive_extremes!(u16);
    test_primitive_extremes!(i32);
    test_primitive_extremes!(u32);
    test_primitive_extremes!(i64);
    test_primitive_extremes!(u64);
    test_primitive_extremes!(i128);
    test_primitive_extremes!(u128);

    Ok(())
}

#[test]
fn optional_key_encoding_tests() -> anyhow::Result<()> {
    assert!(Option::<i8>::None.as_big_endian_bytes()?.is_empty());
    assert_eq!(
        Some(1_i8),
        Option::from_big_endian_bytes(&Some(1_i8).as_big_endian_bytes()?)?
    );
    Ok(())
}

#[test]
#[allow(clippy::unit_cmp)] // this is more of a compilation test
fn unit_key_encoding_tests() -> anyhow::Result<()> {
    assert!(().as_big_endian_bytes()?.is_empty());
    assert_eq!((), <() as Key>::from_big_endian_bytes(&[])?);
    Ok(())
}

#[test]
fn vec_key_encoding_tests() -> anyhow::Result<()> {
    const ORIGINAL_VALUE: &[u8] = b"pliantdb";
    let vec = Cow::<'_, [u8]>::from(ORIGINAL_VALUE);
    assert_eq!(
        vec.clone(),
        Cow::from_big_endian_bytes(&vec.as_big_endian_bytes()?)?
    );
    Ok(())
}