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
//! DomainType trait
//!
//! The DomainType trait allows separation of the data sent on the wire (currently encoded using
//! protobuf) from the structures used in Rust. The structures used to encode/decode from/to the
//! wire are called "Raw" types (they mirror the definitions in the specifications) and the Rust
//! types we use internally are called the "Domain" types. These Domain types can implement
//! additional checks and conversions to consume the incoming data easier for a Rust developer.
//!
//! The benefits include decoding the wire into a struct that is inherently valid as well as hiding
//! the encoding and decoding details from the developer. This latter is important if/when we decide
//! to exchange the underlying Prost library with something else. (Another protobuf implementation
//! or a completely different encoding.) Encoding is not the core product it's a necessary
//! dependency.
//!
//!
//! Decode: bytestream -> Raw -> Domain
//! The `decode` function takes two steps to decode from a bytestream to a DomainType:
//!
//! 1. Decode the bytestream into a Raw type using the Prost library,
//! 2. Transform that Raw type into a Domain type using the TryFrom trait of the DomainType.
//!
//!
//! Encode: Domain -> Raw -> bytestream
//! The `encode` function takes two steps to encode a DomainType into a bytestream:
//!
//! 1. Transform the Domain type into a Raw type using the From trait of the DomainType,
//! 2. Encode the Raw type into a bytestream using the Prost library.
//!
//!
//! Note that in the case of encode, the transformation to Raw type is infallible:
//! Rust structs should always be ready to be encoded to the wire.
//!
//! Note that the Prost library and the TryFrom method have their own set of errors. These are
//! merged into a custom Error type defined in this crate for easier handling.
//!
//! Requirements:
//! * The DomainType trait requires the struct to implement the Clone trait.
//! * Any RawType structure implements the prost::Message trait. (protobuf struct)
//! * The DomainType trait requires that the TryFrom<RawType> implemented on the structure has an
//!   error type that implements Into<BoxError>. (The current implementations with anomaly are
//!   fine.)
//!
//! How to implement a DomainType struct:
//! 1. Implement your struct based on your expectations for the developer
//! 2. Add `impl DomainType<MyRawType> for MyDomainType {}` blanket implementation of the trait
//! 4. Implement the `TryFrom<MyRawType> for MyDomainType` trait
//! 5. Implement the `From<MyDomainType> for MyRawType` trait

use crate::{Error, Kind};
use anomaly::BoxError;
use bytes::{Buf, BufMut};
use prost::{encoding::encoded_len_varint, Message};
use std::convert::{TryFrom, TryInto};

/// DomainType trait allows protobuf encoding and decoding for domain types
pub trait DomainType<T: Message + From<Self> + Default>
where
    Self: Sized + Clone + TryFrom<T>,
    <Self as TryFrom<T>>::Error: Into<BoxError>,
{
    /// Encodes the DomainType into a buffer.
    ///
    /// This function replaces the Prost::Message encode() function for DomainTypes.
    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<(), Error> {
        T::from(self.clone())
            .encode(buf)
            .map_err(|e| Kind::EncodeMessage.context(e).into())
    }

    /// Encodes the DomainType with a length-delimiter to a buffer.
    ///
    /// An error will be returned if the buffer does not have sufficient capacity.
    ///
    /// This function replaces the Prost::Message encode_length_delimited() function for
    /// DomainTypes.
    fn encode_length_delimited<B: BufMut>(&self, buf: &mut B) -> Result<(), Error> {
        T::from(self.clone())
            .encode_length_delimited(buf)
            .map_err(|e| Kind::EncodeMessage.context(e).into())
    }

    /// Decodes an instance of the message from a buffer and then converts it into DomainType.
    ///
    /// The entire buffer will be consumed.
    ///
    /// This function replaces the Prost::Message decode() function for DomainTypes.
    fn decode<B: Buf>(buf: B) -> Result<Self, Error> {
        T::decode(buf).map_or_else(
            |e| Err(Kind::DecodeMessage.context(e).into()),
            |t| Self::try_from(t).map_err(|e| Kind::TryIntoDomainType.context(e).into()),
        )
    }

    /// Decodes a length-delimited instance of the message from the buffer.
    ///
    /// The entire buffer will be consumed.
    ///
    /// This function replaces the Prost::Message decode_length_delimited() function for
    /// DomainTypes.
    fn decode_length_delimited<B: Buf>(buf: B) -> Result<Self, Error> {
        T::decode_length_delimited(buf).map_or_else(
            |e| Err(Kind::DecodeMessage.context(e).into()),
            |t| Self::try_from(t).map_err(|e| Kind::TryIntoDomainType.context(e).into()),
        )
    }

    /// Returns the encoded length of the message without a length delimiter.
    ///
    /// This function replaces the Prost::Message encoded_len() function for DomainTypes.
    fn encoded_len(&self) -> usize {
        T::from(self.clone()).encoded_len()
    }

    /// Encodes the DomainType into a protobuf-encoded Vec<u8>
    fn encode_vec(&self) -> Result<Vec<u8>, Error> {
        let mut wire = Vec::with_capacity(self.encoded_len());
        self.encode(&mut wire).map(|_| wire)
    }

    /// Decodes a protobuf-encoded instance of the message from a Vec<u8> and then converts it into
    /// DomainType.
    fn decode_vec(v: &[u8]) -> Result<Self, Error> {
        Self::decode(v)
    }

    /// Encodes the DomainType with a length-delimiter to a Vec<u8> protobuf-encoded message.
    fn encode_length_delimited_vec(&self) -> Result<Vec<u8>, Error> {
        let len = self.encoded_len();
        let lenu64 = len.try_into().map_err(|e| Kind::EncodeMessage.context(e))?;
        let mut wire = Vec::with_capacity(len + encoded_len_varint(lenu64));
        self.encode_length_delimited(&mut wire).map(|_| wire)
    }

    /// Decodes a protobuf-encoded instance of the message with a length-delimiter from a Vec<u8>
    /// and then converts it into DomainType.
    fn decode_length_delimited_vec(v: &[u8]) -> Result<Self, Error> {
        Self::decode_length_delimited(v)
    }
}