pub(crate) mod flatbuffers;
pub(crate) mod proto3;
use crate::projection::flatbuffers::Packages;
use crate::projection::proto3::{self as proto3_projection, Scalar};
use crate::v2::{
ArrayType, Constraint, Decl, FieldType, MapType, Package, Param, PrimitiveType, ReturnType,
StructDef, TupleType, TypeDef, UnionDef, backing, decl, field_type, return_type,
};
pub use crate::codegen::v1::FbUnboundedCause as UnboundedCause;
pub struct Ctx<'a> {
packages: Packages<'a>,
scope: Vec<&'a Package>,
others_of: Vec<Vec<&'a Package>>,
}
impl<'a> Ctx<'a> {
pub fn new(package: &'a Package, others: &'a [&'a Package]) -> Self {
let scope: Vec<&'a Package> = std::iter::once(package)
.chain(others.iter().copied())
.collect();
let others_of = scope
.iter()
.map(|home| {
scope
.iter()
.copied()
.filter(|candidate| candidate.name != home.name)
.collect()
})
.collect();
Self {
packages: Packages { package, others },
scope,
others_of,
}
}
pub fn resolve(&self, home: &'a Package, name: &str) -> Option<(&'a Decl, &'a Package)> {
self.packages.resolve(home, name)
}
pub fn packages(&self) -> Packages<'a> {
self.packages
}
pub fn packages_for(&self, declaring: &Package) -> Option<Packages<'_>> {
let position = self
.scope
.iter()
.position(|candidate| candidate.name == declaring.name)?;
Some(Packages {
package: self.scope[position],
others: &self.others_of[position],
})
}
}
pub enum PayloadShape<'a> {
Named(&'a str),
Field(&'a FieldType),
Params(&'a [Param]),
Return(&'a ReturnType),
}
fn payload_name<'a>(shape: &PayloadShape<'a>) -> Result<&'a str, AbsentCause> {
match shape {
PayloadShape::Named(name) => Ok(name),
PayloadShape::Field(ty) => named_field(ty),
PayloadShape::Params([single]) => match single.r#type.as_ref() {
Some(ty) => named_field(ty),
None => Err(AbsentCause::EncodingUndefined),
},
PayloadShape::Params(_) => Err(AbsentCause::EncodingUndefined),
PayloadShape::Return(ret) => match ret.kind.as_ref() {
Some(return_type::Kind::Value(ty)) => named_field(ty),
Some(return_type::Kind::Fallible(_)) | None => Err(AbsentCause::EncodingUndefined),
},
}
}
fn named_field(ty: &FieldType) -> Result<&str, AbsentCause> {
match ty.kind.as_ref() {
Some(field_type::Kind::Named(name)) => Ok(name),
Some(field_type::Kind::Primitive(primitive)) => {
Err(no_bound_or_undefined(leaf_of_primitive(*primitive)))
}
Some(field_type::Kind::InlineScalar(def)) => {
Err(no_bound_or_undefined(leaf_of_type_def(def)))
}
Some(
field_type::Kind::Tuple(_)
| field_type::Kind::Array(_)
| field_type::Kind::Map(_)
| field_type::Kind::Stream(_),
)
| None => Err(AbsentCause::EncodingUndefined),
}
}
fn no_bound_or_undefined(leaf: Option<Leaf<'_>>) -> AbsentCause {
match leaf {
Some(_) => AbsentCause::EncodingUndefined,
None => AbsentCause::NoBound,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Encoding {
Proto3,
FlatBuffers,
ReprC,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeState {
Bounded(u32),
Unbounded(UnboundedCause),
Absent(AbsentCause),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AbsentCause {
EncodingUndefined,
NoMessage,
RefusedMember,
NoBound,
Overflow,
Unresolved,
}
pub fn size_state<'a>(
home: &'a Package,
shape: &PayloadShape<'_>,
ctx: &Ctx<'a>,
encoding: Encoding,
) -> SizeState {
type Sizer<'a> = fn(&'a Package, &str, &Ctx<'a>) -> SizeState;
let sizer: Sizer<'a> = match encoding {
Encoding::Proto3 => proto3::state as Sizer<'a>,
Encoding::FlatBuffers => flatbuffers::state as Sizer<'a>,
Encoding::ReprC => return SizeState::Absent(AbsentCause::EncodingUndefined),
};
match payload_name(shape) {
Ok(name) => sizer(home, name, ctx),
Err(cause) => SizeState::Absent(cause),
}
}
pub fn string_max_bytes(constraint: Option<&Constraint>) -> Option<u64> {
constraint?.len_max?.checked_mul(4)
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum Leaf<'a> {
Scalar(Scalar),
Blob(u64),
Enum {
min: i64,
max: i64,
retired_in_int32: bool,
},
Struct {
name: &'a str,
def: &'a StructDef,
home: &'a Package,
},
Union {
name: &'a str,
def: &'a UnionDef,
home: &'a Package,
},
Tuple {
def: &'a TupleType,
home: &'a Package,
},
Array {
def: &'a ArrayType,
home: &'a Package,
},
Map {
def: &'a MapType,
home: &'a Package,
},
}
pub(crate) fn leaf_of_field_type<'a>(
ty: &'a FieldType,
home: &'a Package,
ctx: &Ctx<'a>,
) -> Option<Leaf<'a>> {
match ty.kind.as_ref()? {
field_type::Kind::Named(name) => leaf_of_name(name, home, ctx),
field_type::Kind::Primitive(primitive) => leaf_of_primitive(*primitive),
field_type::Kind::InlineScalar(def) => leaf_of_type_def(def),
field_type::Kind::Tuple(def) => Some(Leaf::Tuple { def, home }),
field_type::Kind::Array(def) => Some(Leaf::Array { def, home }),
field_type::Kind::Map(def) => Some(Leaf::Map { def, home }),
field_type::Kind::Stream(_) => None,
}
}
pub(crate) fn leaf_of_name<'a>(name: &str, home: &'a Package, ctx: &Ctx<'a>) -> Option<Leaf<'a>> {
let (decl, declaring) = ctx.resolve(home, name)?;
match decl.kind.as_ref()? {
decl::Kind::TypeDef(def) => leaf_of_type_def(def),
decl::Kind::StructDef(def) => Some(Leaf::Struct {
name: &decl.name,
def,
home: declaring,
}),
decl::Kind::UnionDef(def) => Some(Leaf::Union {
name: &decl.name,
def,
home: declaring,
}),
decl::Kind::EnumDef(def) => {
let values = || def.values.iter().map(|v| v.value);
Some(Leaf::Enum {
min: values().min().unwrap_or(0),
max: values().max().unwrap_or(0),
retired_in_int32: def
.reserved
.iter()
.filter_map(|reserved| reserved.value)
.all(|retired| i32::try_from(retired).is_ok()),
})
}
decl::Kind::EnumSetDef(def) => {
Some(Leaf::Scalar(proto3_projection::enum_set_scalar(def.width)))
}
decl::Kind::ConstDef(_)
| decl::Kind::SignalDef(_)
| decl::Kind::EventDef(_)
| decl::Kind::CommandDef(_)
| decl::Kind::QueryDef(_)
| decl::Kind::FixedDef(_)
| decl::Kind::ReservedSlot(_) => None,
}
}
pub(crate) fn leaf_of_primitive<'a>(primitive: i32) -> Option<Leaf<'a>> {
match proto3_projection::primitive(primitive) {
Scalar::String | Scalar::Bytes => None,
scalar => Some(Leaf::Scalar(scalar)),
}
}
fn leaf_of_type_def<'a>(def: &'a TypeDef) -> Option<Leaf<'a>> {
match proto3_projection::scalar(def) {
Scalar::Bytes => def.constraint.as_ref()?.len_max.map(Leaf::Blob),
Scalar::String => match def.backing.as_ref()?.kind.as_ref()? {
backing::Kind::Primitive(primitive) if *primitive == PrimitiveType::String as i32 => {
string_max_bytes(def.constraint.as_ref()).map(Leaf::Blob)
}
backing::Kind::Primitive(_) | backing::Kind::Unit(_) => None,
},
scalar => Some(Leaf::Scalar(scalar)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::projection::flatbuffers::max_size;
use crate::v2::{
Backing, CommandDef, ConstDef, EnumDef, EnumSetDef, EnumValue, EventDef, FallibleType,
Field, FixedDef, FloatWidth, IntWidth, Package, Param, PrimitiveType, QueryDef, Reserved,
ReturnType, SignalDef, StreamType, StructMember, TupleField, field_type, return_type,
stream_type, struct_member, type_def,
};
fn constraint(len_max: Option<u64>, pattern: Option<&str>) -> Constraint {
Constraint {
len_max,
pattern: pattern.map(str::to_owned),
..Default::default()
}
}
fn named(name: &str) -> FieldType {
FieldType {
optional: false,
kind: Some(field_type::Kind::Named(name.to_owned())),
}
}
fn primitive(primitive: PrimitiveType) -> FieldType {
FieldType {
optional: false,
kind: Some(field_type::Kind::Primitive(primitive as i32)),
}
}
fn type_def(
backing: backing::Kind,
width: Option<type_def::Width>,
constraint: Option<Constraint>,
) -> TypeDef {
TypeDef {
backing: Some(Backing {
kind: Some(backing),
}),
constraint,
width,
..Default::default()
}
}
fn string_def(len_max: Option<u64>) -> TypeDef {
type_def(
backing::Kind::Primitive(PrimitiveType::String as i32),
None,
len_max.map(|n| constraint(Some(n), None)),
)
}
fn bytes_def(len_max: Option<u64>) -> TypeDef {
type_def(
backing::Kind::Primitive(PrimitiveType::Bytes as i32),
None,
len_max.map(|n| constraint(Some(n), None)),
)
}
fn decl(name: &str, kind: Option<decl::Kind>) -> Decl {
Decl {
name: name.to_owned(),
kind,
..Default::default()
}
}
fn one_field_struct(ty: FieldType) -> StructDef {
StructDef {
members: vec![StructMember {
member: Some(struct_member::Member::Field(Box::new(Field {
name: "f".to_owned(),
ordinal: 1,
r#type: Some(ty),
..Default::default()
}))),
}],
fixed_layout: false,
}
}
fn package(name: &str, decls: Vec<Decl>) -> Package {
Package {
name: name.to_owned(),
decls,
..Default::default()
}
}
fn param(name: &str, ty: Option<FieldType>) -> Param {
Param {
name: name.to_owned(),
r#type: ty,
..Default::default()
}
}
fn enum_def(values: &[i64]) -> EnumDef {
EnumDef {
values: values
.iter()
.map(|&value| EnumValue {
name: format!("V{}", value.unsigned_abs()),
value,
doc: String::new(),
links: Vec::new(),
see: Vec::new(),
since: Vec::new(),
})
.collect(),
reserved: Vec::new(),
}
}
#[test]
fn a_string_counts_four_bytes_per_scalar_value_whatever_its_pattern() {
assert_eq!(string_max_bytes(None), None, "no constraint: unsizable");
assert_eq!(
string_max_bytes(Some(&constraint(None, None))),
None,
"no `len_max`: unsizable"
);
assert_eq!(
string_max_bytes(Some(&constraint(Some(17), None))),
Some(68)
);
assert_eq!(
string_max_bytes(Some(&constraint(Some(17), Some("^[A-Z0-9]{17}$")))),
Some(68)
);
assert_eq!(
string_max_bytes(Some(&constraint(Some(u64::MAX), None))),
None,
"the multiplication does not wrap"
);
}
#[test]
fn string_max_bytes_agrees_with_the_flatbuffers_projection() {
let seventeen = decl("Seventeen", Some(decl::Kind::TypeDef(string_def(Some(17)))));
let zero = decl("Zero", Some(decl::Kind::TypeDef(string_def(Some(0)))));
let package = package("p", vec![seventeen.clone(), zero.clone()]);
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let bound_17 = max_size(ctx.packages(), &seventeen).expect("a bounded string");
let bound_0 = max_size(ctx.packages(), &zero).expect("a bounded string");
assert_eq!(
Some(bound_17 - bound_0),
string_max_bytes(Some(&constraint(Some(17), None)))
);
assert!(matches!(
leaf_of_name("Seventeen", &package, &ctx),
Some(Leaf::Blob(68))
));
}
#[test]
fn an_unbounded_string_or_bytes_is_unsizable_as_in_the_projection() {
let string_no_bound = decl("S", Some(decl::Kind::TypeDef(string_def(None))));
let bytes_no_bound = decl("B", Some(decl::Kind::TypeDef(bytes_def(None))));
let bytes_bound = decl("B32", Some(decl::Kind::TypeDef(bytes_def(Some(32)))));
let bare_string = decl(
"HoldsString",
Some(decl::Kind::StructDef(one_field_struct(primitive(
PrimitiveType::String,
)))),
);
let bare_bytes = decl(
"HoldsBytes",
Some(decl::Kind::StructDef(one_field_struct(primitive(
PrimitiveType::Bytes,
)))),
);
let bare_bool = decl(
"HoldsBool",
Some(decl::Kind::StructDef(one_field_struct(primitive(
PrimitiveType::Boolean,
)))),
);
let package = package(
"p",
vec![
string_no_bound.clone(),
bytes_no_bound.clone(),
bytes_bound.clone(),
bare_string.clone(),
bare_bytes.clone(),
bare_bool.clone(),
],
);
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
assert_eq!(max_size(ctx.packages(), &string_no_bound), None);
assert!(leaf_of_name("S", &package, &ctx).is_none());
assert_eq!(max_size(ctx.packages(), &bytes_no_bound), None);
assert!(leaf_of_name("B", &package, &ctx).is_none());
assert!(max_size(ctx.packages(), &bytes_bound).is_some());
assert!(matches!(
leaf_of_name("B32", &package, &ctx),
Some(Leaf::Blob(32))
));
assert!(max_size(ctx.packages(), &bare_bool).is_some());
assert_eq!(max_size(ctx.packages(), &bare_string), None);
assert!(leaf_of_field_type(&primitive(PrimitiveType::String), &package, &ctx).is_none());
assert_eq!(max_size(ctx.packages(), &bare_bytes), None);
assert!(leaf_of_field_type(&primitive(PrimitiveType::Bytes), &package, &ctx).is_none());
}
#[test]
fn only_a_payload_that_is_one_named_type_is_sized() {
let stream = FieldType {
optional: false,
kind: Some(field_type::Kind::Stream(StreamType {
element: Some(stream_type::Element::Named("Point".to_owned())),
})),
};
let primitive = primitive(PrimitiveType::Integer);
let one = [Param {
name: "at".to_owned(),
r#type: Some(named("Point")),
doc: String::new(),
links: Vec::new(),
see: Vec::new(),
since: Vec::new(),
}];
let two = [
one[0].clone(),
Param {
name: "flag".to_owned(),
r#type: Some(primitive.clone()),
doc: String::new(),
links: Vec::new(),
see: Vec::new(),
since: Vec::new(),
},
];
let untyped_param = [Param {
name: "at".to_owned(),
r#type: None,
doc: String::new(),
links: Vec::new(),
see: Vec::new(),
since: Vec::new(),
}];
let value = ReturnType {
kind: Some(return_type::Kind::Value(named("Point"))),
};
let fallible = ReturnType {
kind: Some(return_type::Kind::Fallible(FallibleType {
ok: "Point".to_owned(),
err: "Coord".to_owned(),
})),
};
let streamed = ReturnType {
kind: Some(return_type::Kind::Value(stream.clone())),
};
let untyped = ReturnType { kind: None };
assert_eq!(payload_name(&PayloadShape::Named("Point")), Ok("Point"));
assert_eq!(
payload_name(&PayloadShape::Field(&named("Point"))),
Ok("Point")
);
assert_eq!(
payload_name(&PayloadShape::Field(&primitive)),
Err(AbsentCause::EncodingUndefined),
"not a named type"
);
assert_eq!(
payload_name(&PayloadShape::Field(&stream)),
Err(AbsentCause::EncodingUndefined),
"a stream: no codec defines its encoding"
);
assert_eq!(payload_name(&PayloadShape::Params(&one)), Ok("Point"));
assert_eq!(
payload_name(&PayloadShape::Params(&two)),
Err(AbsentCause::EncodingUndefined),
"several parameters"
);
assert_eq!(
payload_name(&PayloadShape::Params(&[])),
Err(AbsentCause::EncodingUndefined),
"zero parameters"
);
assert_eq!(
payload_name(&PayloadShape::Params(&untyped_param)),
Err(AbsentCause::EncodingUndefined),
"one parameter with no type"
);
assert_eq!(payload_name(&PayloadShape::Return(&value)), Ok("Point"));
assert_eq!(
payload_name(&PayloadShape::Return(&fallible)),
Err(AbsentCause::EncodingUndefined),
"an inline `T | E`"
);
assert_eq!(
payload_name(&PayloadShape::Return(&streamed)),
Err(AbsentCause::EncodingUndefined)
);
assert_eq!(
payload_name(&PayloadShape::Return(&untyped)),
Err(AbsentCause::EncodingUndefined)
);
}
#[test]
fn a_primitive_leaf_has_its_proto3_width() {
match leaf_of_primitive(PrimitiveType::Integer as i32) {
Some(Leaf::Scalar(s)) => assert_eq!(s.max_encoded_len(), Some(10)),
other => panic!("integer is a scalar leaf, got {other:?}"),
}
assert!(leaf_of_primitive(PrimitiveType::String as i32).is_none());
assert!(leaf_of_primitive(PrimitiveType::Bytes as i32).is_none());
}
#[test]
fn every_primitive_is_a_leaf_or_unsizable() {
assert!(matches!(
leaf_of_primitive(PrimitiveType::Boolean as i32),
Some(Leaf::Scalar(Scalar::Bool))
));
assert!(matches!(
leaf_of_primitive(PrimitiveType::Integer as i32),
Some(Leaf::Scalar(Scalar::Int64))
));
assert!(matches!(
leaf_of_primitive(PrimitiveType::Float as i32),
Some(Leaf::Scalar(Scalar::Double))
));
assert!(leaf_of_primitive(PrimitiveType::String as i32).is_none());
assert!(leaf_of_primitive(PrimitiveType::Bytes as i32).is_none());
assert!(leaf_of_primitive(PrimitiveType::Unspecified as i32).is_none());
assert!(
leaf_of_primitive(99).is_none(),
"an enum tag the IR does not define"
);
}
#[test]
fn a_type_def_leaf_follows_its_backing_and_width() {
let prim = |p: PrimitiveType| backing::Kind::Primitive(p as i32);
let int_width = |w: IntWidth| Some(type_def::Width::IntWidth(w as i32));
let float_width = |w: FloatWidth| Some(type_def::Width::FloatWidth(w as i32));
fn leaf(def: &TypeDef) -> Option<Leaf<'_>> {
leaf_of_type_def(def)
}
assert!(matches!(
leaf(&type_def(prim(PrimitiveType::Boolean), None, None)),
Some(Leaf::Scalar(Scalar::Bool))
));
assert!(matches!(
leaf(&type_def(
prim(PrimitiveType::Integer),
int_width(IntWidth::I16),
None
)),
Some(Leaf::Scalar(Scalar::Sint32))
));
assert!(
leaf(&type_def(prim(PrimitiveType::Integer), None, None)).is_none(),
"an integer backing without a width projects to `string`, which no constraint bounds"
);
assert!(
leaf(&type_def(prim(PrimitiveType::Float), None, None)).is_none(),
"a float backing without a width projects to `string`, which no constraint bounds"
);
assert!(matches!(
leaf(&type_def(
prim(PrimitiveType::Float),
float_width(FloatWidth::F32),
None
)),
Some(Leaf::Scalar(Scalar::Float))
));
assert!(matches!(leaf(&string_def(Some(17))), Some(Leaf::Blob(68))));
assert!(
leaf(&string_def(None)).is_none(),
"no `len_max`: unsizable, as in the projection"
);
assert!(matches!(leaf(&bytes_def(Some(32))), Some(Leaf::Blob(32))));
assert!(
leaf(&bytes_def(None)).is_none(),
"no `len_max`: unsizable, as in the projection"
);
assert!(leaf(&type_def(prim(PrimitiveType::Unspecified), None, None)).is_none());
assert!(matches!(
leaf(&type_def(
prim(PrimitiveType::Boolean),
int_width(IntWidth::U16),
None
)),
Some(Leaf::Scalar(Scalar::Uint32))
));
assert!(matches!(
leaf(&type_def(
prim(PrimitiveType::Integer),
float_width(FloatWidth::F32),
None
)),
Some(Leaf::Scalar(Scalar::Float))
));
assert!(
matches!(
leaf(&type_def(
prim(PrimitiveType::String),
int_width(IntWidth::U8),
Some(constraint(Some(17), None))
)),
Some(Leaf::Scalar(Scalar::Uint32))
),
"a string backing with a width is the width's scalar, not a blob"
);
let unit = || backing::Kind::Unit("km/h".to_owned());
assert!(matches!(
leaf(&type_def(unit(), int_width(IntWidth::U16), None)),
Some(Leaf::Scalar(Scalar::Uint32))
));
assert!(matches!(
leaf(&type_def(unit(), float_width(FloatWidth::F64), None)),
Some(Leaf::Scalar(Scalar::Double))
));
assert!(
leaf(&type_def(unit(), None, None)).is_none(),
"the proto backend emits `string` for a unit without a width"
);
assert!(leaf(&TypeDef::default()).is_none(), "no backing");
assert!(
leaf(&type_def(
prim(PrimitiveType::Bytes),
None,
Some(constraint(None, None))
))
.is_none(),
"a constraint without `len_max`: unsizable"
);
assert!(
matches!(
leaf(&type_def(
prim(PrimitiveType::Integer),
Some(type_def::Width::IntWidth(99)),
None
)),
Some(Leaf::Scalar(Scalar::Int64))
),
"an integer width tag the IR does not define is `int64`, as the backend emits"
);
assert!(
matches!(
leaf(&type_def(
prim(PrimitiveType::Float),
Some(type_def::Width::FloatWidth(99)),
None
)),
Some(Leaf::Scalar(Scalar::Double))
),
"a float width tag the IR does not define is `double`, as the backend emits"
);
}
#[test]
fn every_declaration_kind_resolves_to_a_leaf_or_none() {
let home = package(
"p",
vec![
decl("Speed", Some(decl::Kind::TypeDef(string_def(Some(3))))),
decl("Point", Some(decl::Kind::StructDef(StructDef::default()))),
decl("Shape", Some(decl::Kind::UnionDef(UnionDef::default()))),
decl("Gear", Some(decl::Kind::EnumDef(enum_def(&[-3, 0, 7])))),
decl("Mode", Some(decl::Kind::EnumDef(enum_def(&[0, 5])))),
decl(
"Flags",
Some(decl::Kind::EnumSetDef(EnumSetDef {
width: IntWidth::U8 as i32,
..Default::default()
})),
),
decl("MAX", Some(decl::Kind::ConstDef(ConstDef::default()))),
decl("speed", Some(decl::Kind::SignalDef(SignalDef::default()))),
decl("tick", Some(decl::Kind::EventDef(EventDef::default()))),
decl("go", Some(decl::Kind::CommandDef(CommandDef::default()))),
decl("ask", Some(decl::Kind::QueryDef(QueryDef::default()))),
decl("fixed", Some(decl::Kind::FixedDef(FixedDef::default()))),
decl("gone", Some(decl::Kind::ReservedSlot(Reserved::default()))),
decl("Untyped", None),
decl("Empty", Some(decl::Kind::EnumDef(enum_def(&[])))),
decl(
"OddFlags",
Some(decl::Kind::EnumSetDef(EnumSetDef {
width: 99,
..Default::default()
})),
),
],
);
let other = package(
"q",
vec![decl(
"Thing",
Some(decl::Kind::UnionDef(UnionDef::default())),
)],
);
let others = [&other];
let ctx = Ctx::new(&home, &others);
assert!(matches!(
leaf_of_name("Speed", &home, &ctx),
Some(Leaf::Blob(12))
));
assert!(matches!(
leaf_of_name("Point", &home, &ctx),
Some(Leaf::Struct { home: h, .. }) if h.name == "p"
));
assert!(matches!(
leaf_of_name("Shape", &home, &ctx),
Some(Leaf::Union { home: h, .. }) if h.name == "p"
));
assert!(matches!(
leaf_of_name("Gear", &home, &ctx),
Some(Leaf::Enum {
min: -3,
max: 7,
retired_in_int32: true
})
));
assert!(matches!(
leaf_of_name("Mode", &home, &ctx),
Some(Leaf::Enum {
min: 0,
max: 5,
retired_in_int32: true
})
));
assert!(
matches!(
leaf_of_name("Flags", &home, &ctx),
Some(Leaf::Scalar(Scalar::Uint32))
),
"an enum set is its width's scalar, as `enum_set_field_type` emits"
);
assert!(
matches!(
leaf_of_name("Empty", &home, &ctx),
Some(Leaf::Enum {
min: 0,
max: 0,
retired_in_int32: true
})
),
"an enum with no member has magnitude 0"
);
assert!(
matches!(
leaf_of_name("OddFlags", &home, &ctx),
Some(Leaf::Scalar(Scalar::Int64))
),
"a width tag the IR does not define is `int64`, as the backend emits"
);
for name in [
"MAX", "speed", "tick", "go", "ask", "fixed", "gone", "Untyped",
] {
assert!(
leaf_of_name(name, &home, &ctx).is_none(),
"{name} has no leaf"
);
}
assert!(
leaf_of_name("Nowhere", &home, &ctx).is_none(),
"an unresolved name"
);
assert!(
leaf_of_name("q.Nowhere", &home, &ctx).is_none(),
"an unresolved member of a known package"
);
assert!(
leaf_of_name("r.Thing", &home, &ctx).is_none(),
"an unknown package"
);
assert!(matches!(
leaf_of_name("p.Point", &home, &ctx),
Some(Leaf::Struct { home: h, .. }) if h.name == "p"
));
assert!(matches!(
leaf_of_name("q.Thing", &home, &ctx),
Some(Leaf::Union { home: h, .. }) if h.name == "q"
));
assert!(
leaf_of_name("Thing", &home, &ctx).is_none(),
"a foreign declaration does not resolve by its bare name from p"
);
assert!(matches!(
leaf_of_name("Thing", &other, &ctx),
Some(Leaf::Union { home: h, .. }) if h.name == "q"
));
assert_eq!(ctx.packages().package.name, "p");
assert_eq!(ctx.packages().others.len(), 1);
}
#[test]
fn a_bare_name_inside_an_imported_declaration_resolves_in_its_own_package() {
let root = package(
"p",
vec![
decl("Inner", Some(decl::Kind::StructDef(StructDef::default()))),
decl(
"OnlyInRoot",
Some(decl::Kind::StructDef(StructDef::default())),
),
],
);
let imported = package(
"q",
vec![
decl(
"Thing",
Some(decl::Kind::StructDef(one_field_struct(named("Inner")))),
),
decl("Inner", Some(decl::Kind::EnumDef(enum_def(&[0, 1])))),
],
);
let others = [&imported];
let ctx = Ctx::new(&root, &others);
let (def, home) = match leaf_of_name("q.Thing", &root, &ctx) {
Some(Leaf::Struct { def, home, .. }) => (def, home),
other => panic!("q.Thing is a struct leaf, got {other:?}"),
};
assert_eq!(
home.name, "q",
"the composite carries its declaring package"
);
let field = match &def.members[0].member {
Some(struct_member::Member::Field(field)) => field.r#type.as_ref().unwrap(),
other => panic!("one field, got {other:?}"),
};
assert!(
matches!(
leaf_of_field_type(field, home, &ctx),
Some(Leaf::Enum {
min: 0,
max: 1,
retired_in_int32: true
})
),
"resolved against q, the field is q's enum `Inner`"
);
assert!(matches!(
leaf_of_field_type(field, &root, &ctx),
Some(Leaf::Struct { home: h, .. }) if h.name == "p"
));
assert!(
leaf_of_name("OnlyInRoot", &imported, &ctx).is_none(),
"a bare name only the root declares does not resolve from q"
);
assert!(matches!(
leaf_of_name("OnlyInRoot", &root, &ctx),
Some(Leaf::Struct { home: h, .. }) if h.name == "p"
));
}
#[test]
fn packages_for_lists_the_root_first_then_the_others_in_order() {
let root = package("p", vec![]);
let second = package("q", vec![]);
let third = package("r", vec![]);
let same_name = package(
"p",
vec![decl(
"Shadowed",
Some(decl::Kind::StructDef(StructDef::default())),
)],
);
let others = [&second, &third, &same_name];
let ctx = Ctx::new(&root, &others);
let names = |packages: Packages<'_>| -> Vec<String> {
std::iter::once(packages.package)
.chain(packages.others.iter().copied())
.map(|package| package.name.clone())
.collect()
};
assert_eq!(names(ctx.packages_for(&root).unwrap()), ["p", "q", "r"]);
assert_eq!(
names(ctx.packages_for(&second).unwrap()),
["q", "p", "r", "p"]
);
assert_eq!(
names(ctx.packages_for(&third).unwrap()),
["r", "p", "q", "p"]
);
assert!(
ctx.packages_for(&root)
.unwrap()
.others
.iter()
.all(|other| !std::ptr::eq(*other, &same_name))
);
assert!(ctx.resolve(&second, "p.Shadowed").is_none());
}
#[test]
fn every_field_type_kind_resolves_to_a_leaf_or_none() {
let home = package(
"p",
vec![decl(
"Point",
Some(decl::Kind::StructDef(StructDef::default())),
)],
);
let others: [&Package; 0] = [];
let ctx = Ctx::new(&home, &others);
let field = |kind: field_type::Kind| FieldType {
optional: false,
kind: Some(kind),
};
assert!(matches!(
leaf_of_field_type(&named("Point"), &home, &ctx),
Some(Leaf::Struct { home: h, .. }) if h.name == "p"
));
assert!(matches!(
leaf_of_field_type(&primitive(PrimitiveType::Boolean), &home, &ctx),
Some(Leaf::Scalar(Scalar::Bool))
));
assert!(matches!(
leaf_of_field_type(
&field(field_type::Kind::InlineScalar(Box::new(string_def(Some(
2
))))),
&home,
&ctx
),
Some(Leaf::Blob(8))
));
assert!(matches!(
leaf_of_field_type(
&field(field_type::Kind::Tuple(TupleType {
fields: vec![TupleField {
name: "x".to_owned(),
r#type: Some(named("Point")),
}],
})),
&home,
&ctx
),
Some(Leaf::Tuple { home: h, .. }) if h.name == "p"
));
assert!(matches!(
leaf_of_field_type(
&field(field_type::Kind::Array(Box::new(ArrayType {
element: Some(Box::new(named("Point"))),
min: 0,
max: 4,
}))),
&home,
&ctx
),
Some(Leaf::Array { home: h, .. }) if h.name == "p"
));
assert!(matches!(
leaf_of_field_type(
&field(field_type::Kind::Map(Box::new(MapType {
key: Some(Box::new(named("Point"))),
value: Some(Box::new(named("Point"))),
min: 0,
max: 4,
}))),
&home,
&ctx
),
Some(Leaf::Map { home: h, .. }) if h.name == "p"
));
assert!(
leaf_of_field_type(
&field(field_type::Kind::Stream(StreamType {
element: Some(stream_type::Element::Named("Point".to_owned())),
})),
&home,
&ctx
)
.is_none(),
"a stream has no defined encoding, so its sizes are absent"
);
assert!(
leaf_of_field_type(
&FieldType {
optional: false,
kind: None
},
&home,
&ctx
)
.is_none(),
"no kind"
);
}
#[test]
fn repr_c_is_absent_with_encoding_undefined() {
let package = package(
"p",
vec![decl(
"Point",
Some(decl::Kind::StructDef(one_field_struct(primitive(
PrimitiveType::Boolean,
)))),
)],
);
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
assert_eq!(
size_state(
&package,
&PayloadShape::Named("Point"),
&ctx,
Encoding::ReprC
),
SizeState::Absent(AbsentCause::EncodingUndefined)
);
}
const WIRE: [Encoding; 2] = [Encoding::Proto3, Encoding::FlatBuffers];
#[test]
fn the_home_decides_which_package_a_bare_payload_name_is_read_from() {
let root = package("p", vec![]);
let other = package(
"q",
vec![decl(
"Point",
Some(decl::Kind::StructDef(one_field_struct(primitive(
PrimitiveType::Boolean,
)))),
)],
);
let others = [&other];
let ctx = Ctx::new(&root, &others);
for encoding in WIRE {
assert!(
matches!(
size_state(&other, &PayloadShape::Named("Point"), &ctx, encoding),
SizeState::Bounded(_)
),
"{encoding:?}, read from q"
);
assert_eq!(
size_state(&root, &PayloadShape::Named("Point"), &ctx, encoding),
SizeState::Absent(AbsentCause::Unresolved),
"{encoding:?}, read from p"
);
}
}
#[test]
fn a_named_scalar_has_no_proto3_message() {
let package = tests_support::fixture();
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let shape = PayloadShape::Named("Vin");
assert_eq!(
size_state(&package, &shape, &ctx, Encoding::Proto3),
SizeState::Absent(AbsentCause::NoMessage)
);
assert!(matches!(
size_state(&package, &shape, &ctx, Encoding::FlatBuffers),
SizeState::Bounded(_)
));
}
#[test]
fn a_stream_payload_is_undefined_in_both_encodings() {
let package = tests_support::fixture();
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let stream = FieldType {
optional: false,
kind: Some(field_type::Kind::Stream(StreamType {
element: Some(stream_type::Element::Named("Point".to_owned())),
})),
};
for encoding in WIRE {
assert_eq!(
size_state(&package, &PayloadShape::Field(&stream), &ctx, encoding),
SizeState::Absent(AbsentCause::EncodingUndefined),
"{encoding:?}"
);
}
}
#[test]
fn two_parameters_are_undefined_in_both_encodings() {
let package = tests_support::fixture();
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let params = [param("at", Some(named("Point"))), param("to", None)];
for encoding in WIRE {
assert_eq!(
size_state(&package, &PayloadShape::Params(¶ms), &ctx, encoding),
SizeState::Absent(AbsentCause::EncodingUndefined),
"{encoding:?}"
);
assert_eq!(
size_state(&package, &PayloadShape::Params(&[]), &ctx, encoding),
SizeState::Absent(AbsentCause::EncodingUndefined),
"{encoding:?}, zero parameters"
);
}
}
#[test]
fn a_fallible_reply_is_undefined_in_both_encodings() {
let package = tests_support::fixture();
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let fallible = ReturnType {
kind: Some(return_type::Kind::Fallible(FallibleType {
ok: "Point".to_owned(),
err: "Coord".to_owned(),
})),
};
for encoding in WIRE {
assert_eq!(
size_state(&package, &PayloadShape::Return(&fallible), &ctx, encoding),
SizeState::Absent(AbsentCause::EncodingUndefined),
"{encoding:?}"
);
}
}
#[test]
fn an_unbounded_string_has_no_bound() {
let package = tests_support::fixture();
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let bare = primitive(PrimitiveType::String);
let inline = FieldType {
optional: false,
kind: Some(field_type::Kind::InlineScalar(Box::new(string_def(None)))),
};
for encoding in WIRE {
assert_eq!(
size_state(&package, &PayloadShape::Field(&bare), &ctx, encoding),
SizeState::Absent(AbsentCause::NoBound),
"{encoding:?}, a bare `string`"
);
assert_eq!(
size_state(&package, &PayloadShape::Field(&inline), &ctx, encoding),
SizeState::Absent(AbsentCause::NoBound),
"{encoding:?}, an inline `string` with no bound"
);
}
}
#[test]
fn an_unbounded_string_request_has_no_bound_rather_than_no_encoding() {
let package = tests_support::fixture();
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let params = [param("name", Some(primitive(PrimitiveType::String)))];
for encoding in WIRE {
assert_eq!(
size_state(&package, &PayloadShape::Params(¶ms), &ctx, encoding),
SizeState::Absent(AbsentCause::NoBound),
"{encoding:?}"
);
}
}
#[test]
fn an_unbounded_string_reply_has_no_bound_rather_than_no_encoding() {
let package = tests_support::fixture();
let others: [&Package; 0] = [];
let ctx = Ctx::new(&package, &others);
let value = ReturnType {
kind: Some(return_type::Kind::Value(primitive(PrimitiveType::String))),
};
for encoding in WIRE {
assert_eq!(
size_state(&package, &PayloadShape::Return(&value), &ctx, encoding),
SizeState::Absent(AbsentCause::NoBound),
"{encoding:?}"
);
}
}
#[test]
fn max_size_over_an_imported_declaration_is_rooted_at_its_package() {
let root = package(
"p",
vec![decl(
"Inner",
Some(decl::Kind::StructDef(one_field_struct(FieldType {
optional: false,
kind: Some(field_type::Kind::InlineScalar(Box::new(string_def(Some(
100,
))))),
}))),
)],
);
let imported = package(
"q",
vec![
decl(
"Thing",
Some(decl::Kind::StructDef(one_field_struct(named("Inner")))),
),
decl(
"Inner",
Some(decl::Kind::StructDef(one_field_struct(primitive(
PrimitiveType::Boolean,
)))),
),
],
);
let others = [&imported];
let ctx = Ctx::new(&root, &others);
let (thing, declaring) = ctx.resolve(&root, "q.Thing").expect("q.Thing resolves");
assert_eq!(declaring.name, "q");
let rooted = ctx.packages_for(declaring).expect("q is in scope");
assert_eq!(rooted.package.name, "q");
assert_eq!(rooted.others.len(), 1);
assert_eq!(rooted.others[0].name, "p");
let expected = max_size(
Packages {
package: &imported,
others: &[&root],
},
thing,
)
.expect("a one-boolean struct inside a struct is bounded");
assert_eq!(max_size(rooted, thing), Some(expected));
assert_ne!(
max_size(ctx.packages(), thing),
Some(expected),
"rooted at p, the bare `Inner` is p's wider struct"
);
let at_root = ctx.packages_for(&root).expect("the root is in scope");
assert_eq!(at_root.package.name, "p");
assert_eq!(at_root.others.len(), 1);
assert!(
ctx.packages_for(&package("r", vec![])).is_none(),
"a package outside the scope"
);
}
#[test]
fn an_inline_composite_leaf_carries_the_package_of_its_field() {
let root = package("p", vec![]);
let imported = package(
"q",
vec![decl(
"Point",
Some(decl::Kind::StructDef(StructDef::default())),
)],
);
let others = [&imported];
let ctx = Ctx::new(&root, &others);
let field = |kind: field_type::Kind| FieldType {
optional: false,
kind: Some(kind),
};
let tuple = field(field_type::Kind::Tuple(TupleType {
fields: vec![TupleField {
name: "x".to_owned(),
r#type: Some(named("Point")),
}],
}));
let array = field(field_type::Kind::Array(Box::new(ArrayType {
element: Some(Box::new(named("Point"))),
min: 0,
max: 4,
})));
let map = field(field_type::Kind::Map(Box::new(MapType {
key: Some(Box::new(named("Point"))),
value: Some(Box::new(named("Point"))),
min: 0,
max: 4,
})));
assert!(matches!(
leaf_of_field_type(&tuple, &imported, &ctx),
Some(Leaf::Tuple { home, .. }) if home.name == "q"
));
assert!(matches!(
leaf_of_field_type(&array, &imported, &ctx),
Some(Leaf::Array { home, .. }) if home.name == "q"
));
assert!(matches!(
leaf_of_field_type(&map, &imported, &ctx),
Some(Leaf::Map { home, .. }) if home.name == "q"
));
}
}
#[cfg(test)]
pub(crate) mod tests_support {
use crate::v2::{
ArrayType, Backing, ConstDef, Constraint, Decl, Field, FieldType, IntWidth, MapType,
Package, PrimitiveType, StructDef, StructMember, TypeDef, UnionArm, UnionDef, backing,
decl, field_type, struct_member, type_def,
};
fn scalar_def(
primitive: PrimitiveType,
width: Option<IntWidth>,
constraint: Option<Constraint>,
) -> TypeDef {
TypeDef {
backing: Some(Backing {
kind: Some(backing::Kind::Primitive(primitive as i32)),
}),
constraint,
width: width.map(|w| type_def::Width::IntWidth(w as i32)),
..Default::default()
}
}
pub(crate) fn inline(def: TypeDef) -> FieldType {
FieldType {
optional: false,
kind: Some(field_type::Kind::InlineScalar(Box::new(def))),
}
}
pub(crate) fn named(name: &str) -> FieldType {
FieldType {
optional: false,
kind: Some(field_type::Kind::Named(name.to_owned())),
}
}
pub(crate) fn array_u8_max4() -> FieldType {
FieldType {
optional: false,
kind: Some(field_type::Kind::Array(Box::new(ArrayType {
element: Some(Box::new(inline(scalar_def(
PrimitiveType::Integer,
Some(IntWidth::U8),
None,
)))),
min: 0,
max: 4,
}))),
}
}
pub(crate) fn map_str8_u32_max2() -> FieldType {
let key = inline(scalar_def(
PrimitiveType::String,
None,
Some(Constraint {
len_max: Some(8),
..Default::default()
}),
));
let value = inline(scalar_def(
PrimitiveType::Integer,
Some(IntWidth::U32),
None,
));
FieldType {
optional: false,
kind: Some(field_type::Kind::Map(Box::new(MapType {
key: Some(Box::new(key)),
value: Some(Box::new(value)),
min: 0,
max: 2,
}))),
}
}
pub(crate) fn fixture() -> Package {
let field = |name: &str, ordinal: u32, ty: FieldType| StructMember {
member: Some(struct_member::Member::Field(Box::new(Field {
name: name.to_owned(),
ordinal,
r#type: Some(ty),
..Default::default()
}))),
};
let arm = |name: &str, ordinal: u32, type_ref: &str| UnionArm {
name: name.to_owned(),
ordinal,
type_ref: type_ref.to_owned(),
..Default::default()
};
Package {
name: "p".to_owned(),
decls: vec![
Decl {
name: "Coord".to_owned(),
kind: Some(decl::Kind::TypeDef(scalar_def(
PrimitiveType::Integer,
Some(IntWidth::I16),
None,
))),
..Default::default()
},
Decl {
name: "Point".to_owned(),
kind: Some(decl::Kind::StructDef(StructDef {
members: vec![field("x", 1, named("Coord")), field("y", 2, named("Coord"))],
fixed_layout: false,
})),
..Default::default()
},
Decl {
name: "Vin".to_owned(),
kind: Some(decl::Kind::TypeDef(scalar_def(
PrimitiveType::String,
None,
Some(Constraint {
len_min: Some(17),
len_max: Some(17),
pattern: Some("/^[A-HJ-NPR-Z0-9]{17}$/".to_owned()),
..Default::default()
}),
))),
..Default::default()
},
Decl {
name: "Bag".to_owned(),
kind: Some(decl::Kind::StructDef(StructDef {
members: vec![
field("tag", 1, named("Vin")),
field("flags", 2, array_u8_max4()),
field("index", 3, map_str8_u32_max2()),
],
fixed_layout: false,
})),
..Default::default()
},
Decl {
name: "Shape".to_owned(),
kind: Some(decl::Kind::UnionDef(UnionDef {
arms: vec![arm("a", 1, "Point"), arm("b", 2, "Coord")],
..Default::default()
})),
..Default::default()
},
Decl {
name: "LIMIT".to_owned(),
kind: Some(decl::Kind::ConstDef(ConstDef {
type_ref: Some("Coord".to_owned()),
value: "7".to_owned(),
regex: None,
})),
..Default::default()
},
],
..Default::default()
}
}
pub(crate) fn two_package_fixture() -> (Package, Package) {
let one_field = |name: &str, ty: FieldType| StructDef {
members: vec![StructMember {
member: Some(struct_member::Member::Field(Box::new(Field {
name: name.to_owned(),
ordinal: 1,
r#type: Some(ty),
..Default::default()
}))),
}],
fixed_layout: false,
};
let structure = |name: &str, def: StructDef| Decl {
name: name.to_owned(),
kind: Some(decl::Kind::StructDef(def)),
..Default::default()
};
let boolean = FieldType {
optional: false,
kind: Some(field_type::Kind::Primitive(PrimitiveType::Boolean as i32)),
};
let bare_string = FieldType {
optional: false,
kind: Some(field_type::Kind::Primitive(PrimitiveType::String as i32)),
};
let string_100 = inline(scalar_def(
PrimitiveType::String,
None,
Some(Constraint {
len_max: Some(100),
..Default::default()
}),
));
let root = Package {
name: "p".to_owned(),
decls: vec![
structure("Inner", one_field("s", string_100)),
structure("Hole", one_field("b", boolean.clone())),
],
..Default::default()
};
let imported = Package {
name: "q".to_owned(),
decls: vec![
structure("Thing", one_field("f", named("Inner"))),
structure("Inner", one_field("b", boolean)),
structure("Loose", one_field("f", named("Hole"))),
structure("Hole", one_field("text", bare_string)),
],
..Default::default()
};
(root, imported)
}
pub(crate) fn unbounded_fixture() -> Package {
Package {
name: "q".to_owned(),
decls: vec![Decl {
name: "Open".to_owned(),
kind: Some(decl::Kind::StructDef(StructDef {
members: vec![StructMember {
member: Some(struct_member::Member::Field(Box::new(Field {
name: "text".to_owned(),
ordinal: 1,
r#type: Some(FieldType {
optional: false,
kind: Some(field_type::Kind::Primitive(
PrimitiveType::String as i32,
)),
}),
..Default::default()
}))),
}],
fixed_layout: false,
})),
..Default::default()
}],
..Default::default()
}
}
}