use crate::ProtobufError;
use ::std::convert::TryFrom;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FieldNumber(u32);
impl FieldNumber {
pub const MIN: Self = Self(1);
pub const MAX: Self = Self(536_870_911);
pub const MAX_SINGLE_BYTE_TAG: u32 = 15;
pub const RESERVED_RANGE_START: u32 = 19000;
pub const RESERVED_RANGE_END: u32 = 19999;
pub fn try_new(value: u32) -> Result<Self, ProtobufError> {
if !(Self::MIN.0..=Self::MAX.0).contains(&value) {
return Err(ProtobufError::FieldNumberOutOfRange {
value: value.to_string(),
});
}
Ok(Self(value))
}
pub fn is_tag_single_byte(&self) -> bool {
self.0 <= Self::MAX_SINGLE_BYTE_TAG
}
pub fn is_reserved(&self) -> bool {
self.0 >= Self::RESERVED_RANGE_START && self.0 <= Self::RESERVED_RANGE_END
}
pub fn is_in_range(&self, min: u32, max: u32) -> bool {
self.0 >= min && self.0 <= max
}
pub fn encoded_size(&self) -> usize {
let max_tag_value = (self.0 << 3) | 7;
match max_tag_value {
0..=0x7F => 1,
0x80..=0x3FFF => 2,
0x4000..=0x1FFFFF => 3,
0x200000..=0xFFFFFFF => 4,
_ => 5,
}
}
pub fn as_u32(&self) -> u32 {
self.0
}
pub fn as_i32(&self) -> i32 {
self.0 as i32
}
pub fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl TryFrom<u32> for FieldNumber {
type Error = ProtobufError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::try_new(value)
}
}
impl From<FieldNumber> for u32 {
fn from(field_number: FieldNumber) -> Self {
field_number.as_u32()
}
}
impl TryFrom<i32> for FieldNumber {
type Error = ProtobufError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
Self::try_new(
value
.try_into()
.map_err(|_| ProtobufError::FieldNumberOutOfRange {
value: value.to_string(),
})?,
)
}
}
impl From<FieldNumber> for i32 {
fn from(field_number: FieldNumber) -> Self {
field_number.as_i32()
}
}
impl From<FieldNumber> for usize {
fn from(field_number: FieldNumber) -> Self {
field_number.as_usize()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field_number_creation() {
assert!(FieldNumber::try_new(1).is_ok());
assert!(FieldNumber::try_new(16).is_ok());
assert!(FieldNumber::try_new(536_870_911).is_ok());
assert!(FieldNumber::try_new(0).is_err());
assert!(FieldNumber::try_new(536_870_912).is_err());
}
#[test]
fn test_field_number_conversion() {
let field = FieldNumber::try_new(42).unwrap();
assert_eq!(u32::from(field), 42);
assert_eq!(usize::from(field), 42);
assert_eq!(field.as_u32(), 42);
assert_eq!(field.as_usize(), 42);
}
#[test]
fn test_field_number_try_from() {
let result = FieldNumber::try_from(1);
assert!(result.is_ok());
let field_number = result.unwrap();
assert_eq!(field_number.as_u32(), 1);
let result = FieldNumber::try_from(0);
assert!(result.is_err());
if let Err(ProtobufError::FieldNumberOutOfRange { value }) = result {
assert_eq!(value, "0");
} else {
panic!("Expected FieldNumberOutOfRange error");
}
}
#[test]
fn test_helper_methods() {
let single_byte_field = FieldNumber::try_new(15).unwrap();
let multi_byte_field = FieldNumber::try_new(16).unwrap();
let large_field = FieldNumber::try_new(1000).unwrap();
let reserved_field = FieldNumber::try_new(19500).unwrap();
assert!(single_byte_field.is_tag_single_byte());
assert!(!multi_byte_field.is_tag_single_byte());
assert!(!large_field.is_tag_single_byte());
assert!(reserved_field.is_reserved());
assert!(!single_byte_field.is_reserved());
assert!(single_byte_field.is_in_range(1, 20));
assert!(!single_byte_field.is_in_range(20, 30));
}
#[test]
fn test_encoded_size() {
assert_eq!(FieldNumber::try_new(1).unwrap().encoded_size(), 1);
assert_eq!(FieldNumber::try_new(16).unwrap().encoded_size(), 2);
assert_eq!(FieldNumber::try_new(100).unwrap().encoded_size(), 2);
assert_eq!(FieldNumber::try_new(10000).unwrap().encoded_size(), 3);
assert_eq!(FieldNumber::try_new(1000000).unwrap().encoded_size(), 4);
assert_eq!(FieldNumber::try_new(100000000).unwrap().encoded_size(), 5);
}
#[test]
fn test_as_u32_and_as_usize() {
let min_field = FieldNumber::MIN;
let max_field = FieldNumber::MAX;
let mid_field = FieldNumber::try_new(1000).unwrap();
assert_eq!(min_field.as_u32(), 1);
assert_eq!(max_field.as_u32(), 536_870_911);
assert_eq!(mid_field.as_u32(), 1000);
assert_eq!(min_field.as_usize(), 1);
assert_eq!(max_field.as_usize(), 536_870_911);
assert_eq!(mid_field.as_usize(), 1000);
}
}