diff --git a/src/lib.rs b/src/lib.rs index 2ed94e5..dd34b45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,38 @@ pub fn decode_tag(bytes: &[u8]) -> Result<Option<(u64, Varint, usize)>, Error> { } } +pub trait Schema<'a>: Sized { + fn should_unwrap(element_id: u64) -> bool; + fn decode<'b: 'a>(element_id: u64, bytes: &'b[u8]) -> Result<Self, Error>; +} + +pub fn decode_element<'a, 'b: 'a, T: Schema<'a>>(bytes: &'b[u8]) -> Result<Option<(T, usize)>, Error> { + match decode_tag(bytes) { + Ok(None) => Ok(None), + Err(err) => Err(err), + Ok(Some((element_id, payload_size_tag, tag_size))) => { + let should_unwrap = <T as Schema>::should_unwrap(element_id); + + let payload_size = match (should_unwrap, payload_size_tag) { + (true, _) => 0, + (false, Varint::Unknown) => return Err(Error::UnknownElementLength), + (false, Varint::Value(size)) => size as usize + }; + + let element_size = tag_size + payload_size; + if element_size > bytes.len() { + // need to read more still + return Ok(None); + } + + match Schema::decode(element_id, &bytes[tag_size..element_size]) { + Ok(element) => Ok(Some((element, element_size))), + Err(error) => Err(error) + } + } + } +} + #[cfg(test)] mod tests {