use bytes::BufMut;
use nom::error::{Error, ErrorKind};
use nom::number::complete::{be_u8, be_u16, be_u64};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExtensionField {
Fec2V1(Fec2V1Value),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fec2V1Value {
pub major_version: u8,
pub minor_version: u8,
pub startup_id: u64,
}
impl ExtensionField {
pub const MIN_SIZE: usize = 16;
pub fn field_type(&self) -> u16 {
match self {
ExtensionField::Fec2V1 { .. } => 0xFEC2,
}
}
pub fn length(&self) -> u16 {
match self {
ExtensionField::Fec2V1 { .. } => 16, }
}
pub fn emit_bytes(&self, buffer: &mut [u8]) {
let mut buf = buffer;
buf.put_u16_ne(self.field_type().to_be());
buf.put_u16_ne(self.length().to_be());
match self {
ExtensionField::Fec2V1(fec2v1) => {
buf.put_u8(1); buf.put_u8(fec2v1.major_version);
buf.put_u8(fec2v1.minor_version);
buf.put_u8(0);
buf.put_u64_ne(fec2v1.startup_id.to_be());
}
}
}
pub fn parse_from_bytes(input: &[u8]) -> nom::IResult<&[u8], ExtensionField> {
let (input, field_type) = be_u16(input)?;
let (input, _length) = be_u16(input)?;
match field_type {
0xFEC2 => {
let (input, _ext_version) = be_u8(input)?;
let (input, major_version) = be_u8(input)?;
let (input, minor_version) = be_u8(input)?;
let (input, _) = be_u8(input)?;
let (input, startup_id) = be_u64(input)?;
Ok((
input,
ExtensionField::Fec2V1(Fec2V1Value {
major_version,
minor_version,
startup_id,
}),
))
}
_ => Err(nom::Err::Error(Error::new(input, ErrorKind::Switch))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extension_field_wire_format_emit() {
let ext = ExtensionField::Fec2V1(Fec2V1Value {
major_version: 2,
minor_version: 3,
startup_id: 0x123456789ABCDEF0,
});
let mut buffer = vec![0u8; ext.length() as usize];
ext.emit_bytes(&mut buffer);
let expected = [
0xFE, 0xC2, 0x00, 0x10, 0x01, 0x02, 0x03, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, ];
assert_eq!(buffer, expected);
}
#[test]
fn extension_field_wire_format_parse() {
let wire_bytes = [
0xFE, 0xC2, 0x00, 0x10, 0x01, 0x02, 0x03, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, ];
let (leftover, parsed) = ExtensionField::parse_from_bytes(&wire_bytes).unwrap();
assert!(leftover.is_empty());
match parsed {
ExtensionField::Fec2V1(fec2value) => {
assert_eq!(fec2value.major_version, 2);
assert_eq!(fec2value.minor_version, 3);
assert_eq!(fec2value.startup_id, 0x123456789ABCDEF0);
}
}
}
#[test]
fn extension_field_methods() {
let ext = ExtensionField::Fec2V1(Fec2V1Value {
major_version: 1,
minor_version: 2,
startup_id: 0xFFC0FFEE,
});
assert_eq!(ext.field_type(), 0xFEC2);
assert_eq!(ext.length(), 16);
}
#[test]
fn parse_unknown_field_type_fails() {
let wire_bytes = [
0xFF, 0xFF, 0x00, 0x08, 0x01, 0x02, 0x03, 0x04, ];
let result = ExtensionField::parse_from_bytes(&wire_bytes);
assert!(result.is_err());
}
#[test]
fn extension_field_roundtrip() {
let original = ExtensionField::Fec2V1(Fec2V1Value {
major_version: 1,
minor_version: 2,
startup_id: 0xDEADBEEFCAFEBABE,
});
let mut buffer = vec![0u8; original.length() as usize];
original.emit_bytes(&mut buffer);
let (leftover, parsed) = ExtensionField::parse_from_bytes(&buffer).unwrap();
assert!(leftover.is_empty());
assert_eq!(original, parsed);
}
}