Skip to content

Commit 99c0948

Browse files
authored
Add more description of proto to the proto exercise (#2475)
This also moves the test cases into `main` so they are visible. Fixes #2466.
1 parent c61204a commit 99c0948

File tree

2 files changed

+66
-54
lines changed

2 files changed

+66
-54
lines changed

src/lifetimes/exercise.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,36 @@ message Person {
2929
}
3030
```
3131

32+
## Messages
33+
3234
A proto message is encoded as a series of fields, one after the next. Each is
3335
implemented as a "tag" followed by the value. The tag contains a field number
3436
(e.g., `2` for the `id` field of a `Person` message) and a wire type defining
35-
how the payload should be determined from the byte stream.
37+
how the payload should be determined from the byte stream. These are combined
38+
into a single integer, as decoded in `unpack_tag` below.
39+
40+
## Varint
3641

3742
Integers, including the tag, are represented with a variable-length encoding
38-
called VARINT. Luckily, `parse_varint` is defined for you below. The given code
39-
also defines callbacks to handle `Person` and `PhoneNumber` fields, and to parse
40-
a message into a series of calls to those callbacks.
43+
called VARINT. Luckily, `parse_varint` is defined for you below.
44+
45+
## Wire Types
46+
47+
Proto defines several wire types, only two of which are used in this exercise.
48+
49+
The `Varint` wire type contains a single varint, and is used to encode proto
50+
values of type `int32` such as `Person.id`.
51+
52+
The `Len` wire type contains a length expressed as a varint, followed by a
53+
payload of that number of bytes. This is used to encode proto values of type
54+
`string` such as `Person.name`. It is also used to encode proto values
55+
containing sub-messages such as `Person.phones`, where the payload contains an
56+
encoding of the sub-message.
57+
58+
## Exercise
59+
60+
The given code also defines callbacks to handle `Person` and `PhoneNumber`
61+
fields, and to parse a message into a series of calls to those callbacks.
4162

4263
What remains for you is to implement the `parse_field` function and the
4364
`ProtoMessage` trait for `Person` and `PhoneNumber`.

src/lifetimes/exercise.rs

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
enum WireType {
1919
/// The Varint WireType indicates the value is a single VARINT.
2020
Varint,
21-
/// The I64 WireType indicates that the value is precisely 8 bytes in
22-
/// little-endian order containing a 64-bit signed integer or double type.
21+
// The I64 WireType indicates that the value is precisely 8 bytes in
22+
// little-endian order containing a 64-bit signed integer or double type.
2323
//I64, -- not needed for this exercise
2424
/// The Len WireType indicates that the value is a length represented as a
2525
/// VARINT followed by exactly that number of bytes.
@@ -195,6 +195,34 @@ impl<'a> ProtoMessage<'a> for PhoneNumber<'a> {
195195

196196
// ANCHOR: main
197197
fn main() {
198+
let person_id: Person = parse_message(&[0x10, 0x2a]);
199+
assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] });
200+
201+
let person_name: Person = parse_message(&[
202+
0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20,
203+
0x6e, 0x61, 0x6d, 0x65,
204+
]);
205+
assert_eq!(person_name, Person { name: "beautiful name", id: 0, phone: vec![] });
206+
207+
let person_name_id: Person =
208+
parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]);
209+
assert_eq!(person_name_id, Person { name: "Evan", id: 22, phone: vec![] });
210+
211+
let phone: Person = parse_message(&[
212+
0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33,
213+
0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04,
214+
0x68, 0x6f, 0x6d, 0x65,
215+
]);
216+
assert_eq!(
217+
phone,
218+
Person {
219+
name: "",
220+
id: 0,
221+
phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "home" },],
222+
}
223+
);
224+
225+
// Put that all together into a single parse.
198226
let person: Person = parse_message(&[
199227
0x0a, 0x07, 0x6d, 0x61, 0x78, 0x77, 0x65, 0x6c, 0x6c, 0x10, 0x2a, 0x1a,
200228
0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x30, 0x32, 0x2d, 0x35, 0x35, 0x35,
@@ -203,53 +231,16 @@ fn main() {
203231
0x2d, 0x35, 0x33, 0x30, 0x38, 0x12, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c,
204232
0x65,
205233
]);
206-
println!("{:#?}", person);
234+
assert_eq!(
235+
person,
236+
Person {
237+
name: "maxwell",
238+
id: 42,
239+
phone: vec![
240+
PhoneNumber { number: "+1202-555-1212", type_: "home" },
241+
PhoneNumber { number: "+1800-867-5308", type_: "mobile" },
242+
]
243+
}
244+
);
207245
}
208246
// ANCHOR_END: main
209-
210-
#[cfg(test)]
211-
mod tests {
212-
use super::*;
213-
214-
#[test]
215-
fn test_id() {
216-
let person_id: Person = parse_message(&[0x10, 0x2a]);
217-
assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] });
218-
}
219-
220-
#[test]
221-
fn test_name() {
222-
let person_name: Person = parse_message(&[
223-
0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20,
224-
0x6e, 0x61, 0x6d, 0x65,
225-
]);
226-
assert_eq!(
227-
person_name,
228-
Person { name: "beautiful name", id: 0, phone: vec![] }
229-
);
230-
}
231-
232-
#[test]
233-
fn test_just_person() {
234-
let person_name_id: Person =
235-
parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]);
236-
assert_eq!(person_name_id, Person { name: "Evan", id: 22, phone: vec![] });
237-
}
238-
239-
#[test]
240-
fn test_phone() {
241-
let phone: Person = parse_message(&[
242-
0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33,
243-
0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04,
244-
0x68, 0x6f, 0x6d, 0x65,
245-
]);
246-
assert_eq!(
247-
phone,
248-
Person {
249-
name: "",
250-
id: 0,
251-
phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "home" },],
252-
}
253-
);
254-
}
255-
}

0 commit comments

Comments
 (0)