1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-03-20 06:21:09 +02:00

Add more description of proto to the proto exercise (#2475)

This also moves the test cases into `main` so they are visible.

Fixes #2466.
This commit is contained in:
Dustin J. Mitchell 2024-12-05 10:17:14 -05:00 committed by GitHub
parent c61204ae18
commit 99c0948f4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 66 additions and 54 deletions

View File

@ -29,15 +29,36 @@ message Person {
}
```
## Messages
A proto message is encoded as a series of fields, one after the next. Each is
implemented as a "tag" followed by the value. The tag contains a field number
(e.g., `2` for the `id` field of a `Person` message) and a wire type defining
how the payload should be determined from the byte stream.
how the payload should be determined from the byte stream. These are combined
into a single integer, as decoded in `unpack_tag` below.
## Varint
Integers, including the tag, are represented with a variable-length encoding
called VARINT. Luckily, `parse_varint` is defined for you below. The given code
also defines callbacks to handle `Person` and `PhoneNumber` fields, and to parse
a message into a series of calls to those callbacks.
called VARINT. Luckily, `parse_varint` is defined for you below.
## Wire Types
Proto defines several wire types, only two of which are used in this exercise.
The `Varint` wire type contains a single varint, and is used to encode proto
values of type `int32` such as `Person.id`.
The `Len` wire type contains a length expressed as a varint, followed by a
payload of that number of bytes. This is used to encode proto values of type
`string` such as `Person.name`. It is also used to encode proto values
containing sub-messages such as `Person.phones`, where the payload contains an
encoding of the sub-message.
## Exercise
The given code also defines callbacks to handle `Person` and `PhoneNumber`
fields, and to parse a message into a series of calls to those callbacks.
What remains for you is to implement the `parse_field` function and the
`ProtoMessage` trait for `Person` and `PhoneNumber`.

View File

@ -18,8 +18,8 @@
enum WireType {
/// The Varint WireType indicates the value is a single VARINT.
Varint,
/// The I64 WireType indicates that the value is precisely 8 bytes in
/// little-endian order containing a 64-bit signed integer or double type.
// The I64 WireType indicates that the value is precisely 8 bytes in
// little-endian order containing a 64-bit signed integer or double type.
//I64, -- not needed for this exercise
/// The Len WireType indicates that the value is a length represented as a
/// VARINT followed by exactly that number of bytes.
@ -195,6 +195,34 @@ impl<'a> ProtoMessage<'a> for PhoneNumber<'a> {
// ANCHOR: main
fn main() {
let person_id: Person = parse_message(&[0x10, 0x2a]);
assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] });
let person_name: Person = parse_message(&[
0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20,
0x6e, 0x61, 0x6d, 0x65,
]);
assert_eq!(person_name, Person { name: "beautiful name", id: 0, phone: vec![] });
let person_name_id: Person =
parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]);
assert_eq!(person_name_id, Person { name: "Evan", id: 22, phone: vec![] });
let phone: Person = parse_message(&[
0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33,
0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04,
0x68, 0x6f, 0x6d, 0x65,
]);
assert_eq!(
phone,
Person {
name: "",
id: 0,
phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "home" },],
}
);
// Put that all together into a single parse.
let person: Person = parse_message(&[
0x0a, 0x07, 0x6d, 0x61, 0x78, 0x77, 0x65, 0x6c, 0x6c, 0x10, 0x2a, 0x1a,
0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x30, 0x32, 0x2d, 0x35, 0x35, 0x35,
@ -203,53 +231,16 @@ fn main() {
0x2d, 0x35, 0x33, 0x30, 0x38, 0x12, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c,
0x65,
]);
println!("{:#?}", person);
assert_eq!(
person,
Person {
name: "maxwell",
id: 42,
phone: vec![
PhoneNumber { number: "+1202-555-1212", type_: "home" },
PhoneNumber { number: "+1800-867-5308", type_: "mobile" },
]
}
);
}
// ANCHOR_END: main
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id() {
let person_id: Person = parse_message(&[0x10, 0x2a]);
assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] });
}
#[test]
fn test_name() {
let person_name: Person = parse_message(&[
0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20,
0x6e, 0x61, 0x6d, 0x65,
]);
assert_eq!(
person_name,
Person { name: "beautiful name", id: 0, phone: vec![] }
);
}
#[test]
fn test_just_person() {
let person_name_id: Person =
parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]);
assert_eq!(person_name_id, Person { name: "Evan", id: 22, phone: vec![] });
}
#[test]
fn test_phone() {
let phone: Person = parse_message(&[
0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33,
0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04,
0x68, 0x6f, 0x6d, 0x65,
]);
assert_eq!(
phone,
Person {
name: "",
id: 0,
phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "home" },],
}
);
}
}