-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
332 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,110 @@ | ||
#[allow(unused_imports)] | ||
use protocol::Parcel; | ||
|
||
#[derive(Protocol, Debug, PartialEq, Eq)] | ||
pub enum BoatKind { | ||
Speedboat { warp_speed_enabled: bool }, | ||
Dingy(u8, u8), | ||
Fart, | ||
} | ||
#[cfg(test)] | ||
mod string_discriminants { | ||
#[allow(unused_imports)] | ||
use protocol::Parcel; | ||
|
||
#[test] | ||
fn named_fields_are_correctly_written() { | ||
assert_eq!(vec![0, 0, 0, 1, 1], BoatKind::Speedboat { | ||
warp_speed_enabled: true, | ||
}.raw_bytes().unwrap()); | ||
} | ||
#[derive(Protocol, Clone, Debug, PartialEq)] | ||
#[protocol] | ||
pub enum PlayerState { | ||
Stationary, | ||
Flying { velocity: (f32,f32,f32) }, | ||
Jumping { height: f32 }, | ||
} | ||
|
||
#[test] | ||
fn unnamed_fields_are_correctly_written() { | ||
assert_eq!(vec![0, 0, 0, 2, // discriminator | ||
0xf1, 0xed], BoatKind::Dingy(0xf1, 0xed).raw_bytes().unwrap()); | ||
} | ||
#[derive(Protocol, Debug, PartialEq)] | ||
#[protocol(discriminant = "string")] | ||
pub enum Axis { X, Y, Z, Other(String), Bimp { val: u64 } } | ||
|
||
#[test] | ||
fn unit_variants_are_correctly_written() { | ||
assert_eq!(vec![0, 0, 0, 3], // discriminator | ||
BoatKind::Fart.raw_bytes().unwrap()); | ||
} | ||
#[derive(Protocol, Debug, PartialEq)] | ||
#[protocol(discriminant = "string")] | ||
pub enum RenamedVariant { | ||
Hello, | ||
#[protocol(name = "Universe")] | ||
World, | ||
} | ||
|
||
#[test] | ||
fn named_fields_are_correctly_read() { | ||
assert_eq!(BoatKind::Speedboat { | ||
warp_speed_enabled: true, | ||
}, BoatKind::from_raw_bytes(&[0, 0, 0, 1, 1]).unwrap()); | ||
} | ||
fn verify_read_back<P: Parcel + ::std::fmt::Debug + ::std::cmp::PartialEq>(parcel: P) { | ||
let read_back = P::from_raw_bytes(&parcel.raw_bytes().unwrap()[..]).unwrap(); | ||
assert_eq!(parcel, read_back); | ||
} | ||
|
||
#[test] | ||
fn variant_names_are_discriminators() { | ||
assert_eq!(vec![0, 0, 0, 1, 'X' as _], Axis::X.raw_bytes().unwrap()); | ||
assert_eq!(vec![0, 0, 0, 5, 'O' as _, 't' as _, 'h' as _, 'e' as _, 'r' as _, | ||
0, 0, 0, 4, 'r' as _, 'o' as _, 'l' as _, 'l' as _], | ||
Axis::Other("roll".to_owned()).raw_bytes().unwrap()); | ||
} | ||
|
||
#[test] | ||
fn can_write_and_read_back() { | ||
verify_read_back(Axis::Other("boop".to_owned())); | ||
verify_read_back(Axis::X); | ||
verify_read_back(Axis::Y); | ||
verify_read_back(Axis::Bimp { val: 77 }); | ||
} | ||
|
||
#[test] | ||
fn renamed_variants_are_transmitted() { | ||
assert_eq!(vec![0, 0, 0, 5, 'H' as _, 'e' as _, 'l' as _, 'l' as _, 'o' as _], RenamedVariant::Hello.raw_bytes().unwrap()); | ||
assert_eq!(vec![0, 0, 0, 8, 'U' as _, 'n' as _, 'i' as _, 'v' as _, 'e' as _, 'r' as _, 's' as _, 'e' as _], RenamedVariant::World.raw_bytes().unwrap()); | ||
} | ||
|
||
#[test] | ||
fn unnamed_fields_are_correctly_read() { | ||
assert_eq!(BoatKind::Dingy(99, 78), | ||
BoatKind::from_raw_bytes(&[0, 0, 0, 2, 99, 78]).unwrap()); | ||
#[test] | ||
fn renamed_variants_can_be_written_and_read_back() { | ||
verify_read_back(RenamedVariant::World); | ||
} | ||
} | ||
|
||
#[test] | ||
fn unit_variants_are_correctly_read() { | ||
assert_eq!(BoatKind::Fart, | ||
BoatKind::from_raw_bytes(&[0, 0, 0, 3]).unwrap()); | ||
#[cfg(test)] | ||
mod integer_discriminants { | ||
#[allow(unused_imports)] | ||
use protocol::Parcel; | ||
|
||
#[derive(Protocol, Debug, PartialEq, Eq)] | ||
#[protocol(discriminant = "integer")] | ||
pub enum BoatKind { | ||
Speedboat { warp_speed_enabled: bool }, | ||
Dingy(u8, u8), | ||
Fart, | ||
} | ||
|
||
#[test] | ||
fn named_fields_are_correctly_written() { | ||
assert_eq!(vec![0, 0, 0, 1, 1], BoatKind::Speedboat { | ||
warp_speed_enabled: true, | ||
}.raw_bytes().unwrap()); | ||
} | ||
|
||
#[test] | ||
fn unnamed_fields_are_correctly_written() { | ||
assert_eq!(vec![0, 0, 0, 2, // discriminator | ||
0xf1, 0xed], BoatKind::Dingy(0xf1, 0xed).raw_bytes().unwrap()); | ||
} | ||
|
||
#[test] | ||
fn unit_variants_are_correctly_written() { | ||
assert_eq!(vec![0, 0, 0, 3], // discriminator | ||
BoatKind::Fart.raw_bytes().unwrap()); | ||
} | ||
|
||
#[test] | ||
fn named_fields_are_correctly_read() { | ||
assert_eq!(BoatKind::Speedboat { | ||
warp_speed_enabled: true, | ||
}, BoatKind::from_raw_bytes(&[0, 0, 0, 1, 1]).unwrap()); | ||
} | ||
|
||
#[test] | ||
fn unnamed_fields_are_correctly_read() { | ||
assert_eq!(BoatKind::Dingy(99, 78), | ||
BoatKind::from_raw_bytes(&[0, 0, 0, 2, 99, 78]).unwrap()); | ||
} | ||
|
||
#[test] | ||
fn unit_variants_are_correctly_read() { | ||
assert_eq!(BoatKind::Fart, | ||
BoatKind::from_raw_bytes(&[0, 0, 0, 3]).unwrap()); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use format; | ||
|
||
use syn; | ||
|
||
/// Gets the discriminant format of an enum. | ||
pub fn discriminant_format<F: format::Format>(attrs: &[syn::Attribute]) -> Option<F> { | ||
helper::protocol_meta_name_value_literal("discriminant", attrs).map(helper::expect_lit_str).map(|format_name| { | ||
match F::from_str(&format_name) { | ||
Ok(f) => f, | ||
Err(..) => panic!("invalid enum discriminant format: '{}'", format_name), | ||
} | ||
}) | ||
} | ||
|
||
/// Gets the name as per the attributes. | ||
pub fn name(attrs: &[syn::Attribute]) -> Option<String> { | ||
helper::protocol_meta_name_value_literal("name", attrs).map(helper::expect_lit_str) | ||
} | ||
|
||
mod helper { | ||
use syn; | ||
use proc_macro2; | ||
|
||
pub fn protocol_meta_list(attrs: &[syn::Attribute]) -> Option<syn::MetaList> { | ||
attrs.iter().filter_map(|attr| match attr.interpret_meta() { | ||
Some(syn::Meta::List(meta_list)) => { | ||
if meta_list.ident == syn::Ident::new("protocol", proc_macro2::Span::call_site()) { | ||
Some(meta_list) | ||
} else { | ||
// Unrelated attribute. | ||
None | ||
} | ||
}, | ||
_ => None, | ||
}).next() | ||
} | ||
|
||
pub fn protocol_meta_nested_name_values(attrs: &[syn::Attribute]) -> Vec<syn::MetaNameValue> { | ||
protocol_meta_list(attrs).map(|meta_list| { | ||
let name_values: Vec<_> = meta_list.nested.iter(). | ||
map(|n| match n { | ||
syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) => nv.clone(), | ||
_ => panic!("attribute must look like #[protocol(name = \"value\")]"), | ||
}).collect(); | ||
name_values | ||
}).unwrap_or_else(|| Vec::new()) | ||
} | ||
|
||
pub fn protocol_meta_name_value_literal(meta_name: &str, attrs: &[syn::Attribute]) -> Option<syn::Lit> { | ||
protocol_meta_nested_name_values(attrs).iter().filter_map(|name_value| { | ||
if name_value.ident == syn::Ident::new(meta_name, proc_macro2::Span::call_site()) { | ||
Some(name_value.lit.clone()) | ||
} else { | ||
None // Different meta_name | ||
} | ||
}).next() | ||
} | ||
|
||
pub fn expect_lit_str(lit: syn::Lit) -> String { | ||
match lit { | ||
syn::Lit::Str(s) => s.value(), | ||
_ => panic!("expected a string literal"), | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
//! Different protocol formats. | ||
use attr; | ||
use syn; | ||
|
||
pub type Discriminator = u32; | ||
|
||
/// Represents a format. | ||
pub trait Format : Clone { | ||
/// From a string. | ||
fn from_str(s: &str) -> Result<Self, ()>; | ||
} | ||
|
||
/// The enum protocol format. | ||
#[derive(Clone, Debug, PartialEq)] | ||
pub enum Enum { | ||
/// The enum is transmitted by using the 1-based index of the enum variant. | ||
IntegerDiscriminator, | ||
/// The enum is transmitted by using the name of the variant. | ||
StringDiscriminator, | ||
} | ||
|
||
impl Enum { | ||
/// Gets the discriminator of the enum. | ||
pub fn discriminator(&self, e: &syn::DataEnum, | ||
variant: &syn::Variant) -> ::proc_macro2::TokenStream { | ||
match *self { | ||
Enum::IntegerDiscriminator => { | ||
let variant_index = e.variants.iter().position(|v| v.ident == variant.ident).expect("variant not a part of enum"); | ||
|
||
let discriminator = match variant.discriminant { | ||
Some((_, syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(ref n), .. }))) => n.value() as Discriminator, | ||
Some(_) => panic!("unknown discriminator"), | ||
// Reserve discriminator 0. | ||
None => variant_index as Discriminator + 1, | ||
}; | ||
|
||
quote!(#discriminator) | ||
}, | ||
Enum::StringDiscriminator => { | ||
let variant_name = attr::name(&variant.attrs).unwrap_or_else(|| variant.ident.to_string()); | ||
quote! { String::from(#variant_name) } | ||
}, | ||
} | ||
} | ||
|
||
pub fn discriminator_for_pattern_matching(&self) -> ::proc_macro2::TokenStream { | ||
match *self { | ||
Enum::IntegerDiscriminator => quote!(discriminator), | ||
Enum::StringDiscriminator => quote!(&discriminator[..]), | ||
} | ||
} | ||
|
||
pub fn discriminator_variant_for_pattern_matching(&self, e: &syn::DataEnum, | ||
variant: &syn::Variant) -> ::proc_macro2::TokenStream { | ||
match *self { | ||
Enum::IntegerDiscriminator => self.discriminator(e, variant), | ||
Enum::StringDiscriminator => { | ||
let variant_name = attr::name(&variant.attrs).unwrap_or_else(|| variant.ident.to_string()); | ||
quote! { #variant_name } | ||
}, | ||
} | ||
} | ||
|
||
pub fn discriminator_type(&self) -> ::proc_macro2::TokenStream { | ||
match *self { | ||
Enum::IntegerDiscriminator => { | ||
let s = syn::Ident::new(&format!("u{}", ::std::mem::size_of::<Discriminator>() * 8), ::proc_macro2::Span::call_site()); | ||
quote!(#s) | ||
}, | ||
Enum::StringDiscriminator => quote!(String), | ||
} | ||
} | ||
} | ||
|
||
impl Format for Enum { | ||
fn from_str(s: &str) -> Result<Self, ()> { | ||
match s { | ||
"integer" => Ok(Enum::IntegerDiscriminator), | ||
"string" => Ok(Enum::StringDiscriminator), | ||
_ => Err(()), | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.