Skip to content

Commit

Permalink
feat(xdr): initial commit for rust xdr serialization work
Browse files Browse the repository at this point in the history
This repo is a response to the limitations found in the serde format and
rust language when handling XDR serialization. The end goal is to allow
a user to manipulate normal rust objects that can than be properly
serialized into the XDR format.

The primary hangup with serde and other implementations is the lack of
support for fixed length xdr array values. The built in rust array type
is quite limited. See:
https://stackoverflow.com/questions/30901965/implement-debug-trait-for-large-array-type
serde-rs/serde#573

To work around this I've used Vec<T> as the type for both fixed and
variable length xdr arrays allowing a user to add a macro attribute to
distinguish between fixed/variable length while defaulting to a max
sized variable length array.

Current progress is tracked in the README.md for now, with most of the
serialization work completed and all of the deserialization work to go
still.

Breaks nothing.
  • Loading branch information
kbartush committed May 31, 2019
0 parents commit 7d7446c
Show file tree
Hide file tree
Showing 7 changed files with 516 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "ex-dee"
version = "0.1.0"
authors = ["Kyle Bartush <[email protected]>"]
edition = "2018"

[dependencies]
ex-dee-derive = { path = "ex-dee-derive" }
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Spec Notes

## Types

- Integer [ser]
- Unsigned Integer [ser]
- Enumeration []
- Boolean [ser]
- Hyper Integer [ser]
- Hyper Unsigned Integer [ser]
- Floating-Point [ser]
- Double-Precision Floating-Point [ser]
- Quadruple-Precision Floating Point []
- Fixed Length Opaque [ser]
- Variable-Length Opaque [ser]
- String [ser]
- Fixed-Length Array [ser]
- Variable-Length Array [ser]
- Structure [ser]
- Discriminated Union []
- Void [ser]
- Optional-Data []
3 changes: 3 additions & 0 deletions ex-dee-derive/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock
13 changes: 13 additions & 0 deletions ex-dee-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "ex-dee-derive"
version = "0.1.0"
authors = ["Kyle Bartush <[email protected]>"]
edition = "2018"

[dependencies]
syn = { version = "0.15.12" , features = ["full", "extra-traits", "parsing"] }
quote = "0.6.8"
proc-macro2 = "0.4"

[lib]
proc-macro = true
105 changes: 105 additions & 0 deletions ex-dee-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#![recursion_limit = "128"]

extern crate proc_macro;

use crate::proc_macro::TokenStream;
use quote::quote;
use syn;
use syn::Meta::{List, NameValue};
use syn::NestedMeta::Meta;

#[proc_macro_derive(XDROut, attributes(array))]
pub fn xdr_out_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();

impl_xdr_out_macro(&ast)
}

#[derive(Debug)]
struct Member {
pub name: proc_macro2::Ident,
pub fixed: u32,
pub var: u32,
}

fn get_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "array" {
match attr.interpret_meta() {
Some(List(ref meta)) => Some(meta.nested.iter().cloned().collect()),
_ => None,
}
} else {
None
}
}

fn get_members(fields: &syn::Fields) -> Result<Vec<Member>, ()> {
match fields {
syn::Fields::Named(ref named) => {
let mut result = Vec::new();
for field in named.named.iter() {
let mut fixed: u32 = 0;
let mut var: u32 = 0;
for meta_items in field.attrs.iter().filter_map(get_meta_items) {
for meta_item in meta_items {
match meta_item {
Meta(NameValue(ref m)) if m.ident == "fixed" => match m.lit {
syn::Lit::Int(ref val) => {
fixed = val.value() as u32;
}
_ => {}
},
Meta(NameValue(ref m)) if m.ident == "var" => match m.lit {
syn::Lit::Int(ref val) => {
var = val.value() as u32;
}
_ => {}
},
_ => {}
};
}
}
result.push(Member {
name: field.ident.clone().unwrap(),
fixed: fixed,
var: var,
});
}
Ok(result)
}
_ => Err(()),
}
}

fn impl_xdr_out_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let members = match &ast.data {
syn::Data::Struct(data_struct) => get_members(&data_struct.fields).unwrap(),
_ => panic!("Contract macro only works with trait declarations!"),
};
let calls: Vec<proc_macro2::TokenStream> = members
.iter()
.map(|i| match (&i.name, i.fixed, i.var) {
(name, 0, 0) => format!("written += self.{}.write_xdr(out)?;", name)
.parse()
.unwrap(),
(name, fixed, 0) => format!(
"written += write_fixed_array(&self.{}, {}, out)?;",
name, fixed
)
.parse()
.unwrap(),
_ => "asdf".to_string().parse().unwrap(),
})
.collect();
let gen = quote! {
impl<Out: Write> XDROut<Out> for #name {
fn write_xdr(&self, out: &mut Out) -> Result<u64, Error> {
let mut written: u64 = 0;
#(#calls)*
Ok(written)
}
}
};
gen.into()
}
Loading

0 comments on commit 7d7446c

Please sign in to comment.