Skip to content

Commit a629f73

Browse files
committed
Add fixed length attribute
1 parent ae808cf commit a629f73

File tree

4 files changed

+60
-6
lines changed

4 files changed

+60
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
target
22
Cargo.lock
3+
.idea

protocol-derive/src/attr.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub enum Protocol {
1212
prefix_field_name: syn::Ident,
1313
prefix_subfield_names: Vec<syn::Ident>,
1414
},
15+
FixedLength(usize),
1516
}
1617

1718
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -56,6 +57,18 @@ pub fn protocol(attrs: &[syn::Attribute])
5657
Some(syn::NestedMeta::Meta(syn::Meta::List(nested_list))) => {
5758
match &nested_list.path.get_ident().expect("meta is not an ident").to_string()[..] {
5859
// #[protocol(length_prefix(<kind>(<prefix field name>)))]
60+
"fixed_length" => {
61+
let nested_list = expect::meta_list::single_literal(nested_list)
62+
.expect("expected a nested list");
63+
64+
match nested_list {
65+
syn::Lit::Int(len) => {
66+
let len = len.base10_parse::<usize>().expect("Invalid fixed length, expected usize");
67+
Some(Protocol::FixedLength(len))
68+
}
69+
_ => panic!("Invalid fixed length, expected usize")
70+
}
71+
}
5972
"length_prefix" => {
6073
let nested_list = expect::meta_list::nested_list(nested_list)
6174
.expect("expected a nested list");

protocol-derive/src/codegen/mod.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ pub fn write_fields(fields: &syn::Fields)
2828
///
2929
/// Returns `{ ..field initializers.. }`.
3030
fn read_named_fields(fields_named: &syn::FieldsNamed)
31-
-> TokenStream {
31+
-> TokenStream {
3232
let field_initializers: Vec<_> = fields_named.named.iter().map(|field| {
3333
let field_name = &field.ident;
3434
let field_ty = &field.ty;
3535
// This field may store the length prefix of another field.
3636
let update_hints = update_hints_after_read(field, &fields_named.named);
37+
let update_hints_fixed = update_hints__fixed_after_read(field, &fields_named.named);
3738

3839
quote! {
3940
#field_name : {
41+
#update_hints_fixed
4042
let res: Result<#field_ty, _> = protocol::Parcel::read_field(__io_reader, __settings, &mut __hints);
4143
#update_hints
4244
__hints.next_field();
@@ -50,17 +52,32 @@ fn read_named_fields(fields_named: &syn::FieldsNamed)
5052

5153
fn update_hints_after_read<'a>(field: &'a syn::Field,
5254
fields: impl IntoIterator<Item=&'a syn::Field> + Clone)
53-
-> TokenStream {
55+
-> TokenStream {
5456
if let Some((length_prefix_of, kind, prefix_subfield_names)) = length_prefix_of(field, fields.clone()) {
5557
let kind = kind.path_expr();
5658

5759
quote! {
5860
if let Ok(parcel) = res.as_ref() {
59-
__hints.set_field_length(#length_prefix_of,
60-
(parcel #(.#prefix_subfield_names)* ).clone() as usize,
61-
#kind);
61+
__hints.set_field_length(#length_prefix_of,
62+
(parcel #(.#prefix_subfield_names)* ).clone() as usize,
63+
#kind);
6264
}
6365
}
66+
}else {
67+
quote! { }
68+
}
69+
}
70+
71+
fn update_hints__fixed_after_read<'a>(field: &'a syn::Field,
72+
fields: impl IntoIterator<Item=&'a syn::Field> + Clone)
73+
-> TokenStream {
74+
75+
if let Some(attr::Protocol::FixedLength(length)) = attr::protocol(&field.attrs) {
76+
let position = fields.clone().into_iter().position(|f| f == field).unwrap();
77+
78+
quote! {
79+
__hints.set_field_length(#position, #length, protocol::hint::LengthPrefixKind::Elements);
80+
}
6481
} else {
6582
quote! { }
6683
}
@@ -123,14 +140,16 @@ fn length_prefix_of<'a>(field: &'a syn::Field,
123140
}
124141

125142
fn write_named_fields(fields_named: &syn::FieldsNamed)
126-
-> TokenStream {
143+
-> TokenStream {
127144
let field_writers: Vec<_> = fields_named.named.iter().map(|field| {
128145
let field_name = &field.ident;
129146
// This field may store the length prefix of another field.
130147
let update_hints = update_hints_after_write(field, &fields_named.named);
148+
let update_hints_fixed = update_hints__fixed_after_read(field, &fields_named.named);
131149

132150
quote! {
133151
{
152+
#update_hints_fixed
134153
let res = protocol::Parcel::write_field(&self. #field_name, __io_writer, __settings, &mut __hints);
135154
#update_hints
136155
__hints.next_field();

tests/src/length_prefix.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ pub struct WithElementsLength {
2929
pub data: Vec<u32>,
3030
}
3131

32+
#[derive(Protocol, Debug, PartialEq, Eq)]
33+
pub struct WithFixedLength {
34+
#[protocol(fixed_length(3))]
35+
pub data: Vec<u32>,
36+
}
37+
38+
3239
#[test]
3340
fn can_read_length_prefix_5_bytes_string() {
3441
assert_eq!(Foo {
@@ -63,3 +70,17 @@ fn can_read_length_prefix_3_elements() {
6370
&Settings::default()).unwrap());
6471
}
6572

73+
#[test]
74+
fn can_read_fixed_length_prefix() {
75+
76+
assert_eq!(WithFixedLength {
77+
data: vec![1, 2, 3],
78+
}, WithFixedLength::from_raw_bytes(
79+
&[
80+
0, 0, 0, 1, // 1
81+
0, 0, 0, 2, // 2
82+
0, 0, 0, 3
83+
], // 3
84+
&Settings::default()).unwrap());
85+
}
86+

0 commit comments

Comments
 (0)