diff --git a/src/slices-and-lifetimes/exercise.md b/src/slices-and-lifetimes/exercise.md index f46c314c..a7b522fb 100644 --- a/src/slices-and-lifetimes/exercise.md +++ b/src/slices-and-lifetimes/exercise.md @@ -39,7 +39,8 @@ 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. -What remains for you is to implement the `parse_field` function. +What remains for you is to implement the `parse_field` function and the +`ProtoMessage` trait for `Person` and `PhoneNumber`. <!-- compile_fail because `mdbook test` does not allow use of `thiserror` --> ```rust,editable,compile_fail @@ -56,5 +57,9 @@ What remains for you is to implement the `parse_field` function. {{#include exercise.rs:parse_message }} +{{#include exercise.rs:message_types}} + +// TODO: Implement ProtoMessage for Person and PhoneNumber. + {{#include exercise.rs:main }} ``` diff --git a/src/slices-and-lifetimes/exercise.rs b/src/slices-and-lifetimes/exercise.rs index cea0f4e6..6a59f8c5 100644 --- a/src/slices-and-lifetimes/exercise.rs +++ b/src/slices-and-lifetimes/exercise.rs @@ -62,6 +62,10 @@ struct Field<'a> { value: FieldValue<'a>, } +trait ProtoMessage<'a>: Default + 'a { + fn add_field(&mut self, field: Field<'a>) -> Result<(), Error>; +} + impl TryFrom<u64> for WireType { type Error = Error; @@ -171,56 +175,66 @@ fn parse_field(data: &[u8]) -> Result<(Field, &[u8]), Error> { /// Parse a message in the given data, calling `field_callback` for each field in the message. /// /// The entire input is consumed. -fn parse_message( - mut data: &[u8], - field_callback: impl Fn(Field) -> Result<(), Error>, -) -> Result<(), Error> { +fn parse_message<'a, T: ProtoMessage<'a>>(mut data: &'a [u8]) -> Result<T, Error> { + let mut result = T::default(); while !data.is_empty() { let parsed = parse_field(data)?; - field_callback(parsed.0)?; + result.add_field(parsed.0)?; data = parsed.1; } - Ok(()) + Ok(result) } // ANCHOR_END: parse_message +// ANCHOR: message_types +#[derive(Debug, Default)] +struct PhoneNumber<'a> { + number: &'a str, + type_: &'a str, +} + +#[derive(Debug, Default)] +struct Person<'a> { + name: &'a str, + id: u64, + phone: Vec<PhoneNumber<'a>>, +} +// ANCHOR_END: message_types + +impl<'a> ProtoMessage<'a> for Person<'a> { + fn add_field(&mut self, field: Field<'a>) -> Result<(), Error> { + match field.field_num { + 1 => self.name = field.value.as_string()?, + 2 => self.id = field.value.as_u64()?, + 3 => self.phone.push(parse_message(field.value.as_bytes()?)?), + _ => {} // skip everything else + } + Ok(()) + } +} + +impl<'a> ProtoMessage<'a> for PhoneNumber<'a> { + fn add_field(&mut self, field: Field<'a>) -> Result<(), Error> { + match field.field_num { + 1 => self.number = field.value.as_string()?, + 2 => self.type_ = field.value.as_string()?, + _ => {} // skip everything else + } + Ok(()) + } +} + // ANCHOR: main fn main() { - /// Handle a field in a Person message. - fn person_field(field: Field) -> Result<(), Error> { - match field.field_num { - 1 => println!("name: {}", field.value.as_string()?), - 2 => println!("id: {}", field.value.as_u64()?), - 3 => { - println!("phone:"); - parse_message(field.value.as_bytes()?, phone_number_field)?; - } - _ => {} // skip everything else - } - Ok(()) - } - - /// Handle a field in a PhoneNumber message. - fn phone_number_field(field: Field) -> Result<(), Error> { - match field.field_num { - 1 => println!(" number: {}", field.value.as_string()?), - 2 => println!(" type: {}", field.value.as_string()?), - _ => {} // skip everything else - } - Ok(()) - } - - 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, 0x2d, 0x31, - 0x32, 0x31, 0x32, 0x12, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x1a, 0x18, 0x0a, 0x0e, - 0x2b, 0x31, 0x38, 0x30, 0x30, 0x2d, 0x38, 0x36, 0x37, 0x2d, 0x35, 0x33, 0x30, - 0x38, 0x12, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c, 0x65, - ], - person_field, - ) - .unwrap() + 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, 0x2d, 0x31, + 0x32, 0x31, 0x32, 0x12, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x1a, 0x18, 0x0a, 0x0e, + 0x2b, 0x31, 0x38, 0x30, 0x30, 0x2d, 0x38, 0x36, 0x37, 0x2d, 0x35, 0x33, 0x30, + 0x38, 0x12, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c, 0x65, + ]) + .unwrap(); + println!("{:#?}", person); } // ANCHOR_END: main