Skip to content

Commit

Permalink
feat(protocol): Protobuf serialize (#6)
Browse files Browse the repository at this point in the history
* feat(protocol): Add the underlying data structure.

* chore(rust-toolchain): bump to nightly-2019-08-01

Fix prost compilation failure.

* feat(protocol): add codec trait

* feat(protocol): add primitive protobuf structs

* feat(protocol): implement conversion for protobuf primitive

* feat(protocol): implement ProtocolCodec for protocol primitive

* feat(protocol): add transaction protobuf structs

* feat(protocol): implement conversion for transaction protobuf

* feat(protocol): add *_sync funcs to codec

* chore(protocol): rename BytesCodec to ProtocolCodecSync

* fix(protocol): clippy warning

* chore(protocol): ProtocolCodec::encode now takes mut reference

* chore(protocol): add _sync suffix to ProtocolCodecSync funcs

* chore(protocol): ProtocolCodec now depends on ProtocolCodecSync

Shorten ProtocolCodec trait bound

* chore(protocol): rebase to latest master
  • Loading branch information
zeroqn authored and yejiayu committed Oct 31, 2019
1 parent a382cf0 commit 197c0b9
Show file tree
Hide file tree
Showing 9 changed files with 808 additions and 12 deletions.
1 change: 1 addition & 0 deletions protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ bytes = "0.4"
sha3 = "0.8"
uint = "0.8"
hex = "0.3"
prost = "0.5"
35 changes: 35 additions & 0 deletions protocol/src/codec/macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#[macro_export]
macro_rules! field {
($opt_field:expr, $type:expr, $field:expr) => {
$opt_field.ok_or_else(|| crate::codec::CodecError::MissingField {
r#type: $type,
field: $field,
})
};
}

#[macro_export]
macro_rules! impl_default_bytes_codec_for {
($category:ident, [$($type:ident),+]) => (
use crate::types::$category;

$(
impl ProtocolCodecSync for $category::$type {
fn encode_sync(&self) -> ProtocolResult<Bytes> {
let ser_type = $type::from(self.clone());
let mut buf = Vec::with_capacity(ser_type.encoded_len());

ser_type.encode(&mut buf).map_err(CodecError::from)?;

Ok(Bytes::from(buf))
}

fn decode_sync(bytes: Bytes) -> ProtocolResult<Self> {
let ser_type = $type::decode(bytes).map_err(CodecError::from)?;

$category::$type::try_from(ser_type)
}
}
)+
)
}
79 changes: 74 additions & 5 deletions protocol/src/codec/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,76 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
// TODO: change Vec<u8> to Bytes
// pin: https://github.com/danburkert/prost/pull/190

#[macro_use]
mod r#macro;
pub mod primitive;
pub mod transaction;

use std::error::Error;

use async_trait::async_trait;
use bytes::Bytes;
use derive_more::{Display, From};

use crate::{ProtocolError, ProtocolErrorKind, ProtocolResult};

#[async_trait]
pub trait ProtocolCodec: Sized + Send + ProtocolCodecSync {
// Note: We take mut reference so that it can be pinned. This removes Sync
// requirement.
async fn encode(&mut self) -> ProtocolResult<Bytes>;

async fn decode<B: Into<Bytes> + Send>(bytes: B) -> ProtocolResult<Self>;
}

// Sync version is still useful in some cases, for example, use in Stream.
// This also work around #[async_trait] problem inside macro
#[doc(hidden)]
pub trait ProtocolCodecSync: Sized + Send {
fn encode_sync(&self) -> ProtocolResult<Bytes>;

fn decode_sync(bytes: Bytes) -> ProtocolResult<Self>;
}

#[async_trait]
impl<T: ProtocolCodecSync + 'static> ProtocolCodec for T {
async fn encode(&mut self) -> ProtocolResult<Bytes> {
<T as ProtocolCodecSync>::encode_sync(self)
}

async fn decode<B: Into<Bytes> + Send>(bytes: B) -> ProtocolResult<Self> {
let bytes: Bytes = bytes.into();

<T as ProtocolCodecSync>::decode_sync(bytes)
}
}

#[derive(Debug, From, Display)]
pub enum CodecError {
#[display(fmt = "prost encode: {}", _0)]
ProtobufEncode(prost::EncodeError),

#[display(fmt = "prost decode: {}", _0)]
ProtobufDecode(prost::DecodeError),

#[display(fmt = "{} missing field {}", r#type, field)]
MissingField {
r#type: &'static str,
field: &'static str,
},

#[display(fmt = "invalid contract type {}", _0)]
InvalidContractType(i32),

#[display(fmt = "wrong bytes length: {{ expect: {}, got: {} }}", expect, real)]
WrongBytesLength { expect: usize, real: usize },
}

impl Error for CodecError {}

// TODO: derive macro
impl From<CodecError> for ProtocolError {
fn from(err: CodecError) -> ProtocolError {
ProtocolError::new(ProtocolErrorKind::Codec, Box::new(err))
}
}
Loading

0 comments on commit 197c0b9

Please sign in to comment.