use alloc::string::String;
use alloc::vec::Vec;
use crate::ProtoFieldType;
use crate::reader::{Reader, WireType};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Label {
Optional,
Required,
Repeated,
}
impl Label {
fn from_wire(v: i32) -> Self {
match v {
2 => Self::Required,
3 => Self::Repeated,
_ => Self::Optional,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Field {
pub name: String,
pub number: i32,
pub label: Label,
pub ty: Option<ProtoFieldType>,
pub type_name: String,
pub oneof_index: Option<i32>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Message {
pub name: String,
pub fields: Vec<Field>,
pub nested: Vec<Message>,
pub enums: Vec<Enum>,
pub map_entry: bool,
pub oneofs: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Enum {
pub name: String,
pub values: Vec<(String, i32)>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct File {
pub package: String,
pub messages: Vec<Message>,
pub enums: Vec<Enum>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct FileDescriptorSet {
pub files: Vec<File>,
}
fn read_str(r: &mut Reader<'_>) -> Option<String> {
core::str::from_utf8(r.read_len_delimited()?)
.ok()
.map(String::from)
}
fn parse_message(bytes: &[u8]) -> Option<Message> {
let mut m = Message::default();
let mut r = Reader::new(bytes);
while !r.at_end() {
let (field, wt) = r.read_tag()?;
match (field, wt) {
(1, WireType::Len) => m.name = read_str(&mut r)?, (2, WireType::Len) => m.fields.push(parse_field(r.read_len_delimited()?)?), (3, WireType::Len) => m.nested.push(parse_message(r.read_len_delimited()?)?), (4, WireType::Len) => m.enums.push(parse_enum(r.read_len_delimited()?)?), (7, WireType::Len) => m.map_entry = parse_message_options(r.read_len_delimited()?)?, (8, WireType::Len) => m.oneofs.push(parse_oneof(r.read_len_delimited()?)?), (_, wt) => r.skip(wt)?,
}
}
Some(m)
}
fn parse_field(bytes: &[u8]) -> Option<Field> {
let mut name = String::new();
let mut number = 0i32;
let mut label = Label::Optional;
let mut ty = None;
let mut type_name = String::new();
let mut oneof_index = None;
let mut r = Reader::new(bytes);
while !r.at_end() {
let (field, wt) = r.read_tag()?;
match (field, wt) {
(1, WireType::Len) => name = read_str(&mut r)?, (3, WireType::Varint) => number = i32::try_from(r.read_varint()?).ok()?, (4, WireType::Varint) => {
label = Label::from_wire(i32::try_from(r.read_varint()?).ok()?)
} (5, WireType::Varint) => {
ty = ProtoFieldType::from_wire(i32::try_from(r.read_varint()?).ok()?)
} (6, WireType::Len) => type_name = read_str(&mut r)?, (9, WireType::Varint) => oneof_index = Some(i32::try_from(r.read_varint()?).ok()?), (_, wt) => r.skip(wt)?,
}
}
Some(Field {
name,
number,
label,
ty,
type_name,
oneof_index,
})
}
fn parse_oneof(bytes: &[u8]) -> Option<String> {
let mut name = String::new();
let mut r = Reader::new(bytes);
while !r.at_end() {
let (field, wt) = r.read_tag()?;
match (field, wt) {
(1, WireType::Len) => name = read_str(&mut r)?, (_, wt) => r.skip(wt)?,
}
}
Some(name)
}
fn parse_message_options(bytes: &[u8]) -> Option<bool> {
let mut map_entry = false;
let mut r = Reader::new(bytes);
while !r.at_end() {
let (field, wt) = r.read_tag()?;
match (field, wt) {
(7, WireType::Varint) => map_entry = r.read_varint()? != 0, (_, wt) => r.skip(wt)?,
}
}
Some(map_entry)
}
fn parse_enum(bytes: &[u8]) -> Option<Enum> {
let mut e = Enum::default();
let mut r = Reader::new(bytes);
while !r.at_end() {
let (field, wt) = r.read_tag()?;
match (field, wt) {
(1, WireType::Len) => e.name = read_str(&mut r)?, (2, WireType::Len) => {
let mut vr = Reader::new(r.read_len_delimited()?);
let mut vname = String::new();
let mut vnum = 0i32;
while !vr.at_end() {
let (vf, vwt) = vr.read_tag()?;
match (vf, vwt) {
(1, WireType::Len) => vname = read_str(&mut vr)?,
(2, WireType::Varint) => vnum = i32::try_from(vr.read_varint()?).ok()?,
(_, vwt) => vr.skip(vwt)?,
}
}
e.values.push((vname, vnum));
}
(_, wt) => r.skip(wt)?,
}
}
Some(e)
}
fn parse_file(bytes: &[u8]) -> Option<File> {
let mut f = File::default();
let mut r = Reader::new(bytes);
while !r.at_end() {
let (field, wt) = r.read_tag()?;
match (field, wt) {
(2, WireType::Len) => f.package = read_str(&mut r)?, (4, WireType::Len) => f.messages.push(parse_message(r.read_len_delimited()?)?), (5, WireType::Len) => f.enums.push(parse_enum(r.read_len_delimited()?)?), (_, wt) => r.skip(wt)?,
}
}
Some(f)
}
impl FileDescriptorSet {
#[must_use]
pub fn decode(bytes: &[u8]) -> Option<Self> {
let mut set = Self::default();
let mut r = Reader::new(bytes);
while !r.at_end() {
let (field, wt) = r.read_tag()?;
match (field, wt) {
(1, WireType::Len) => set.files.push(parse_file(r.read_len_delimited()?)?), (_, wt) => r.skip(wt)?,
}
}
Some(set)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
use alloc::vec;
#[test]
fn parses_a_field_descriptor() {
let bytes = [0x0a, 0x01, b'x', 0x18, 0x01, 0x28, 0x05];
let f = parse_field(&bytes).unwrap();
assert_eq!(f.name, "x");
assert_eq!(f.number, 1);
assert_eq!(f.ty, Some(ProtoFieldType::Int32));
assert_eq!(f.label, Label::Optional);
}
#[test]
fn parses_map_entry_option() {
let bytes = [0x0a, 0x01, b'E', 0x3a, 0x02, 0x38, 0x01];
let m = parse_message(&bytes).unwrap();
assert_eq!(m.name, "E");
assert!(m.map_entry);
}
#[test]
fn parses_oneof_index_and_decl() {
let fx = [0x0a, 0x01, b'x', 0x18, 0x01, 0x28, 0x05, 0x48, 0x00];
let od = [0x0a, 0x04, b'b', b'o', b'd', b'y'];
let mut msg = vec![0x0a, 0x01, b'M'];
msg.push(0x12);
msg.push(fx.len() as u8);
msg.extend_from_slice(&fx);
msg.push(0x42);
msg.push(od.len() as u8);
msg.extend_from_slice(&od);
let m = parse_message(&msg).unwrap();
assert_eq!(m.fields[0].oneof_index, Some(0));
assert_eq!(m.oneofs, vec!["body".to_string()]);
}
#[test]
fn parses_a_message_with_two_fields() {
let fa = [0x0a, 0x01, b'a', 0x18, 0x01, 0x28, 0x09];
let fb = [0x0a, 0x01, b'b', 0x18, 0x02, 0x20, 0x03, 0x28, 0x08];
let mut msg = vec![0x0a, 0x01, b'M'];
msg.push(0x12);
msg.push(fa.len() as u8);
msg.extend_from_slice(&fa);
msg.push(0x12);
msg.push(fb.len() as u8);
msg.extend_from_slice(&fb);
let m = parse_message(&msg).unwrap();
assert_eq!(m.name, "M");
assert_eq!(m.fields.len(), 2);
assert_eq!(m.fields[0].name, "a");
assert_eq!(m.fields[0].ty, Some(ProtoFieldType::String));
assert_eq!(m.fields[1].name, "b");
assert_eq!(m.fields[1].number, 2);
assert_eq!(m.fields[1].label, Label::Repeated);
assert_eq!(m.fields[1].ty, Some(ProtoFieldType::Bool));
}
}