use crate::v2;
pub const PROTO_RESERVED: std::ops::RangeInclusive<u32> = 19_000..=19_999;
pub const PROTO_MAX_FIELD_NUMBER: u32 = 536_870_911;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scalar {
Bool,
Int64,
Uint32,
Uint64,
Sint32,
Sint64,
Float,
Double,
String,
Bytes,
}
impl Scalar {
pub const ALL: [Scalar; 10] = [
Scalar::Bool,
Scalar::Int64,
Scalar::Uint32,
Scalar::Uint64,
Scalar::Sint32,
Scalar::Sint64,
Scalar::Float,
Scalar::Double,
Scalar::String,
Scalar::Bytes,
];
#[must_use]
pub fn admitted_as_map_key(self) -> bool {
match self {
Self::Bool
| Self::Int64
| Self::Uint32
| Self::Uint64
| Self::Sint32
| Self::Sint64
| Self::String => true,
Self::Float | Self::Double | Self::Bytes => false,
}
}
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Bool => "bool",
Self::Int64 => "int64",
Self::Uint32 => "uint32",
Self::Uint64 => "uint64",
Self::Sint32 => "sint32",
Self::Sint64 => "sint64",
Self::Float => "float",
Self::Double => "double",
Self::String => "string",
Self::Bytes => "bytes",
}
}
#[must_use]
pub fn max_encoded_len(self) -> Option<u64> {
match self {
Self::Bool => Some(1),
Self::Uint32 | Self::Sint32 => Some(5),
Self::Int64 | Self::Uint64 | Self::Sint64 => Some(10),
Self::Float => Some(4),
Self::Double => Some(8),
Self::String | Self::Bytes => None,
}
}
}
#[must_use]
pub fn scalar(td: &v2::TypeDef) -> Scalar {
match &td.width {
Some(v2::type_def::Width::IntWidth(width)) => match v2::IntWidth::try_from(*width) {
Ok(v2::IntWidth::U8 | v2::IntWidth::U16 | v2::IntWidth::U32) => Scalar::Uint32,
Ok(v2::IntWidth::U64) => Scalar::Uint64,
Ok(v2::IntWidth::I8 | v2::IntWidth::I16 | v2::IntWidth::I32) => Scalar::Sint32,
Ok(v2::IntWidth::I64) => Scalar::Sint64,
_ => Scalar::Int64,
},
Some(v2::type_def::Width::FloatWidth(width)) => match v2::FloatWidth::try_from(*width) {
Ok(v2::FloatWidth::F32) => Scalar::Float,
_ => Scalar::Double,
},
None => match td
.backing
.as_ref()
.and_then(|backing| backing.kind.as_ref())
{
Some(v2::backing::Kind::Primitive(primitive)) => {
match v2::PrimitiveType::try_from(*primitive) {
Ok(v2::PrimitiveType::Boolean) => Scalar::Bool,
Ok(v2::PrimitiveType::Bytes) => Scalar::Bytes,
_ => Scalar::String,
}
}
_ => Scalar::String,
},
}
}
#[must_use]
pub fn enum_set_scalar(width: i32) -> Scalar {
scalar(&v2::TypeDef {
width: Some(v2::type_def::Width::IntWidth(width)),
..Default::default()
})
}
#[must_use]
pub fn primitive(primitive: i32) -> Scalar {
match v2::PrimitiveType::try_from(primitive) {
Ok(v2::PrimitiveType::Boolean) => Scalar::Bool,
Ok(v2::PrimitiveType::Integer) => Scalar::Int64,
Ok(v2::PrimitiveType::Float) => Scalar::Double,
Ok(v2::PrimitiveType::Bytes) => Scalar::Bytes,
_ => Scalar::String,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::v2::{Backing, FloatWidth, IntWidth, PrimitiveType, TypeDef, backing, type_def};
fn with_int_width(width: i32) -> TypeDef {
TypeDef {
width: Some(type_def::Width::IntWidth(width)),
..Default::default()
}
}
fn with_float_width(width: i32) -> TypeDef {
TypeDef {
width: Some(type_def::Width::FloatWidth(width)),
..Default::default()
}
}
fn with_backing(kind: backing::Kind) -> TypeDef {
TypeDef {
backing: Some(Backing { kind: Some(kind) }),
..Default::default()
}
}
fn prim(primitive: PrimitiveType) -> backing::Kind {
backing::Kind::Primitive(primitive as i32)
}
#[test]
fn the_width_table_is_the_one_the_proto_backend_emits() {
for width in [IntWidth::U8, IntWidth::U16, IntWidth::U32] {
assert_eq!(scalar(&with_int_width(width as i32)).as_str(), "uint32");
}
assert_eq!(
scalar(&with_int_width(IntWidth::U64 as i32)).as_str(),
"uint64"
);
for width in [IntWidth::I8, IntWidth::I16, IntWidth::I32] {
assert_eq!(scalar(&with_int_width(width as i32)).as_str(), "sint32");
}
assert_eq!(
scalar(&with_int_width(IntWidth::I64 as i32)).as_str(),
"sint64"
);
assert_eq!(
scalar(&with_int_width(IntWidth::Unspecified as i32)).as_str(),
"int64"
);
assert_eq!(
scalar(&with_int_width(99)).as_str(),
"int64",
"an integer width tag the IR does not define"
);
assert_eq!(
scalar(&with_float_width(FloatWidth::F32 as i32)).as_str(),
"float"
);
assert_eq!(
scalar(&with_float_width(FloatWidth::F64 as i32)).as_str(),
"double"
);
assert_eq!(
scalar(&with_float_width(FloatWidth::Unspecified as i32)).as_str(),
"double"
);
assert_eq!(
scalar(&with_float_width(99)).as_str(),
"double",
"a float width tag the IR does not define"
);
}
#[test]
fn without_a_width_the_backing_decides() {
assert_eq!(
scalar(&with_backing(prim(PrimitiveType::Boolean))).as_str(),
"bool"
);
assert_eq!(
scalar(&with_backing(prim(PrimitiveType::Bytes))).as_str(),
"bytes"
);
assert_eq!(
scalar(&with_backing(prim(PrimitiveType::String))).as_str(),
"string"
);
assert_eq!(
scalar(&with_backing(prim(PrimitiveType::Integer))).as_str(),
"string",
"an integer backing without a width"
);
assert_eq!(
scalar(&with_backing(prim(PrimitiveType::Float))).as_str(),
"string",
"a float backing without a width"
);
assert_eq!(
scalar(&with_backing(prim(PrimitiveType::Unspecified))).as_str(),
"string"
);
assert_eq!(
scalar(&with_backing(backing::Kind::Unit("km/h".to_owned()))).as_str(),
"string",
"a unit backing without a derived width"
);
assert_eq!(scalar(&TypeDef::default()).as_str(), "string", "no backing");
assert_eq!(
scalar(&TypeDef {
width: Some(type_def::Width::IntWidth(IntWidth::U16 as i32)),
..with_backing(prim(PrimitiveType::Boolean))
})
.as_str(),
"uint32"
);
}
#[test]
fn a_bare_primitive_has_its_own_table() {
assert_eq!(primitive(PrimitiveType::Boolean as i32).as_str(), "bool");
assert_eq!(primitive(PrimitiveType::Integer as i32).as_str(), "int64");
assert_eq!(primitive(PrimitiveType::Float as i32).as_str(), "double");
assert_eq!(primitive(PrimitiveType::Bytes as i32).as_str(), "bytes");
assert_eq!(primitive(PrimitiveType::String as i32).as_str(), "string");
assert_eq!(
primitive(PrimitiveType::Unspecified as i32).as_str(),
"string"
);
assert_eq!(
primitive(99).as_str(),
"string",
"a primitive tag the IR does not define"
);
}
#[test]
fn an_enum_set_is_the_scalar_of_its_width() {
assert_eq!(enum_set_scalar(IntWidth::U8 as i32), Scalar::Uint32);
assert_eq!(enum_set_scalar(IntWidth::U64 as i32), Scalar::Uint64);
assert_eq!(enum_set_scalar(IntWidth::I16 as i32), Scalar::Sint32);
assert_eq!(enum_set_scalar(IntWidth::Unspecified as i32), Scalar::Int64);
assert_eq!(enum_set_scalar(99), Scalar::Int64);
}
#[test]
fn the_map_key_set_is_the_integral_and_string_scalars() {
let admitted: Vec<&str> = Scalar::ALL
.iter()
.filter(|s| s.admitted_as_map_key())
.map(|s| s.as_str())
.collect();
assert_eq!(
admitted,
[
"bool", "int64", "uint32", "uint64", "sint32", "sint64", "string"
]
);
assert_eq!(Scalar::ALL.len(), 10, "every scalar is listed");
}
#[test]
fn the_largest_encoding_of_each_scalar() {
assert_eq!(Scalar::Bool.max_encoded_len(), Some(1));
assert_eq!(Scalar::Uint32.max_encoded_len(), Some(5));
assert_eq!(Scalar::Sint32.max_encoded_len(), Some(5));
assert_eq!(Scalar::Int64.max_encoded_len(), Some(10));
assert_eq!(Scalar::Uint64.max_encoded_len(), Some(10));
assert_eq!(Scalar::Sint64.max_encoded_len(), Some(10));
assert_eq!(Scalar::Float.max_encoded_len(), Some(4));
assert_eq!(Scalar::Double.max_encoded_len(), Some(8));
assert_eq!(Scalar::String.max_encoded_len(), None);
assert_eq!(Scalar::Bytes.max_encoded_len(), None);
}
}