use crate::facet::VendorKind;
use crate::node::type_::TypeKind;
use crate::profile::Profile;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verdict {
Allowed,
Rejected {
rule: &'static str,
spec_reference: &'static str,
detail: &'static str,
},
}
impl Verdict {
#[must_use]
pub const fn is_allowed(self) -> bool {
matches!(self, Self::Allowed)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ConstructKind {
Primitive,
NarrowInteger,
LongDouble,
Fixed,
String,
WideString,
Sequence,
Array1D,
ArrayMultiD,
Map,
Structure,
Union,
Enumeration,
Bitmask,
Bitset,
Alias,
Valuetype,
ValueBox,
Native,
Any,
ValueBase,
ObjectRef,
AnnotationType,
}
impl ConstructKind {
pub const ALL: [Self; 23] = [
Self::Primitive,
Self::NarrowInteger,
Self::LongDouble,
Self::Fixed,
Self::String,
Self::WideString,
Self::Sequence,
Self::Array1D,
Self::ArrayMultiD,
Self::Map,
Self::Structure,
Self::Union,
Self::Enumeration,
Self::Bitmask,
Self::Bitset,
Self::Alias,
Self::Valuetype,
Self::ValueBox,
Self::Native,
Self::Any,
Self::ValueBase,
Self::ObjectRef,
Self::AnnotationType,
];
#[must_use]
pub fn of(kind: &TypeKind) -> Self {
use crate::node::type_::{CharWidth, PrimitiveKind};
match kind {
TypeKind::Primitive(PrimitiveKind::Int8 | PrimitiveKind::UInt8) => Self::NarrowInteger,
TypeKind::Primitive(PrimitiveKind::Float128) => Self::LongDouble,
TypeKind::Primitive(_) => Self::Primitive,
TypeKind::Fixed { .. } => Self::Fixed,
TypeKind::String {
width: CharWidth::Char,
..
} => Self::String,
TypeKind::String {
width: CharWidth::WChar,
..
} => Self::WideString,
TypeKind::Sequence { .. } => Self::Sequence,
TypeKind::Array { dims, .. } if dims.len() > 1 => Self::ArrayMultiD,
TypeKind::Array { .. } => Self::Array1D,
TypeKind::Map { .. } => Self::Map,
TypeKind::Structure { .. } => Self::Structure,
TypeKind::Union { .. } => Self::Union,
TypeKind::Enumeration { .. } => Self::Enumeration,
TypeKind::Bitmask { .. } => Self::Bitmask,
TypeKind::Bitset { .. } => Self::Bitset,
TypeKind::Alias { .. } => Self::Alias,
TypeKind::Valuetype { .. } => Self::Valuetype,
TypeKind::ValueBox { .. } => Self::ValueBox,
TypeKind::Native => Self::Native,
TypeKind::Any => Self::Any,
TypeKind::ValueBase => Self::ValueBase,
TypeKind::ObjectRef { .. } => Self::ObjectRef,
TypeKind::AnnotationType { .. } => Self::AnnotationType,
}
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Primitive => "a primitive",
Self::NarrowInteger => "int8/uint8",
Self::LongDouble => "long double",
Self::Fixed => "fixed<>",
Self::String => "string",
Self::WideString => "wstring",
Self::Sequence => "sequence<>",
Self::Array1D => "a one-dimensional array",
Self::ArrayMultiD => "a multi-dimensional array",
Self::Map => "map<>",
Self::Structure => "struct",
Self::Union => "union",
Self::Enumeration => "enum",
Self::Bitmask => "bitmask",
Self::Bitset => "bitset",
Self::Alias => "typedef",
Self::Valuetype => "valuetype",
Self::ValueBox => "a value box",
Self::Native => "native",
Self::Any => "any",
Self::ValueBase => "ValueBase",
Self::ObjectRef => "an object reference",
Self::AnnotationType => "an annotation type",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum FacetProperty {
Key,
Optional,
External,
MustUnderstand,
NonSerialized,
TryConstruct,
NonFinalExtensibility,
ExplicitMemberId,
DefaultValue,
RangeConstraint,
Unit,
Verbatim,
Ros2DefaultValue,
VendorDirective,
}
impl FacetProperty {
pub const ALL: [Self; 14] = [
Self::Key,
Self::Optional,
Self::External,
Self::MustUnderstand,
Self::NonSerialized,
Self::TryConstruct,
Self::NonFinalExtensibility,
Self::ExplicitMemberId,
Self::DefaultValue,
Self::RangeConstraint,
Self::Unit,
Self::Verbatim,
Self::Ros2DefaultValue,
Self::VendorDirective,
];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Key => "@key",
Self::Optional => "@optional",
Self::External => "@external",
Self::MustUnderstand => "@must_understand",
Self::NonSerialized => "@non_serialized",
Self::TryConstruct => "@try_construct",
Self::NonFinalExtensibility => "@appendable or @mutable",
Self::ExplicitMemberId => "an explicit member id",
Self::DefaultValue => "@default",
Self::RangeConstraint => "@range/@min/@max",
Self::Unit => "@unit",
Self::Verbatim => "@verbatim",
Self::Ros2DefaultValue => "a ROS 2 default value",
Self::VendorDirective => "a vendor directive",
}
}
}
const fn corba_no(detail: &'static str) -> Verdict {
Verdict::Rejected {
rule: "construct exists in CORBA 3.3",
spec_reference: "CORBA 3.3 ยง7.4.1",
detail,
}
}
const fn xtypes_no(detail: &'static str) -> Verdict {
Verdict::Rejected {
rule: "construct exists in DDS-XTypes 1.3",
spec_reference: "DDS-XTypes 1.3 ยง7.2.2",
detail,
}
}
const fn ros_no(detail: &'static str) -> Verdict {
Verdict::Rejected {
rule: "construct expressible in a ROS 2 interface file",
spec_reference: "ROS 2 interface definition rules",
detail,
}
}
const fn idl_no(detail: &'static str) -> Verdict {
Verdict::Rejected {
rule: "construct exists in IDL 4.2",
spec_reference: "OMG IDL 4.2 ยง7.4",
detail,
}
}
const fn flat_no(detail: &'static str) -> Verdict {
Verdict::Rejected {
rule: "plain type",
spec_reference: "zerodds-zero-copy-1.0 ยง3",
detail,
}
}
const fn xml_no(detail: &'static str) -> Verdict {
Verdict::Rejected {
rule: "construct expressible in DDS-XML",
spec_reference: "DDS-XML 1.0 ยง7.4",
detail,
}
}
#[must_use]
pub const fn construct_verdict(profile: Profile, construct: ConstructKind) -> Verdict {
use ConstructKind as C;
match profile {
Profile::Idl42Core => match construct {
C::Primitive
| C::NarrowInteger
| C::LongDouble
| C::Fixed
| C::String
| C::WideString
| C::Sequence
| C::Array1D
| C::ArrayMultiD
| C::Map
| C::Structure
| C::Union
| C::Enumeration
| C::Bitmask
| C::Bitset
| C::Alias
| C::Valuetype
| C::ValueBox
| C::Native
| C::Any
| C::ValueBase
| C::ObjectRef
| C::AnnotationType => Verdict::Allowed,
},
Profile::XTypes13 => match construct {
C::Primitive
| C::NarrowInteger
| C::LongDouble
| C::Fixed
| C::String
| C::WideString
| C::Sequence
| C::Array1D
| C::ArrayMultiD
| C::Map
| C::Structure
| C::Union
| C::Enumeration
| C::Bitmask
| C::Bitset
| C::Alias => Verdict::Allowed,
C::Valuetype => xtypes_no("valuetype has no XTypes type identifier"),
C::ValueBox => xtypes_no("a value box has no XTypes type identifier"),
C::Native => xtypes_no("native is opaque and has no wire form"),
C::Any => xtypes_no("XTypes has no `any`, and no type identifier for one"),
C::ValueBase => xtypes_no("XTypes has no valuetype, so it has no base for one"),
C::ObjectRef => xtypes_no("an object reference is not a data type"),
C::AnnotationType => xtypes_no("an annotation type is metadata, not a topic type"),
},
Profile::Corba33 => match construct {
C::Primitive
| C::LongDouble
| C::Fixed
| C::String
| C::WideString
| C::Sequence
| C::Array1D
| C::ArrayMultiD
| C::Structure
| C::Union
| C::Enumeration
| C::Alias
| C::Valuetype
| C::ValueBox
| C::Native
| C::Any
| C::ValueBase
| C::ObjectRef => Verdict::Allowed,
C::NarrowInteger => corba_no("int8 and uint8 were introduced by IDL 4.2"),
C::Map => corba_no("map<> was introduced by XTypes and has no CORBA equivalent"),
C::Bitmask => corba_no("bitmask was introduced by XTypes"),
C::Bitset => corba_no("bitset was introduced by XTypes"),
C::AnnotationType => corba_no("annotations were introduced by IDL 4.0"),
},
Profile::Ros2 => match construct {
C::Primitive
| C::NarrowInteger
| C::String
| C::Sequence
| C::Array1D
| C::Structure
| C::Alias => Verdict::Allowed,
C::Enumeration => ros_no(
"ROS 2's type model has no enumeration โ its IDL parses `enum` \
and then reports the name as unknown",
),
C::LongDouble => ros_no("ROS 2 has no long double"),
C::Fixed => ros_no("ROS 2 has no fixed-point type"),
C::WideString => ros_no("ROS 2 has no wide string"),
C::ArrayMultiD => ros_no("ROS 2 arrays are one-dimensional"),
C::Map => ros_no("ROS 2 message definitions have no map"),
C::Union => ros_no("ROS 2 message definitions have no union"),
C::Bitmask => ros_no("ROS 2 has no bitmask"),
C::Bitset => ros_no("ROS 2 has no bitset"),
C::Valuetype => ros_no("ROS 2 message definitions have no valuetype"),
C::ValueBox => ros_no("ROS 2 message definitions have no value box"),
C::Native => ros_no("ROS 2 has no native type"),
C::Any => ros_no("ROS 2 message definitions have no `any`"),
C::ValueBase => ros_no("ROS 2 message definitions have no valuetype"),
C::ObjectRef => ros_no("ROS 2 has no object reference"),
C::AnnotationType => ros_no("ROS 2 has no annotation type"),
},
Profile::DdsXml => match construct {
C::Primitive
| C::NarrowInteger
| C::LongDouble
| C::Fixed
| C::String
| C::WideString
| C::Sequence
| C::Array1D
| C::ArrayMultiD
| C::Map
| C::Structure
| C::Union
| C::Enumeration
| C::Bitmask
| C::Bitset
| C::Alias => Verdict::Allowed,
C::Valuetype => xml_no("DDS-XML has no valuetype element"),
C::ValueBox => xml_no("DDS-XML has no value box element"),
C::Native => xml_no("DDS-XML cannot describe an opaque native type"),
C::Any => xml_no("DDS-XML has no `any` element"),
C::ValueBase => xml_no("DDS-XML has no valuetype element to have a base for"),
C::ObjectRef => xml_no("DDS-XML has no object reference element"),
C::AnnotationType => xml_no("DDS-XML has no annotation type element"),
},
Profile::FlatData => match construct {
C::Primitive
| C::NarrowInteger
| C::LongDouble
| C::Fixed
| C::Array1D
| C::ArrayMultiD
| C::Structure
| C::Enumeration
| C::Bitmask
| C::Bitset
| C::Alias => Verdict::Allowed,
C::String => flat_no("a plain type cannot reach a string"),
C::WideString => flat_no("a plain type cannot reach a wide string"),
C::Sequence => flat_no("a plain type cannot reach a sequence"),
C::Map => flat_no("a plain type cannot reach a map"),
C::Union => flat_no("a union's active branch is not fixed"),
C::Valuetype => flat_no("a valuetype is not loanable"),
C::ValueBox => flat_no("a value box carries indirection"),
C::Native => flat_no("native has no known size"),
C::Any => flat_no("`any` decides what it holds at run time, so it has no fixed size"),
C::ValueBase => flat_no("which valuetype arrives is decided at run time"),
C::ObjectRef => flat_no("an object reference is a handle, not data"),
C::AnnotationType => flat_no("an annotation type carries no sample data"),
},
Profile::Vendor(_) => match construct {
C::Primitive
| C::NarrowInteger
| C::LongDouble
| C::Fixed
| C::String
| C::WideString
| C::Sequence
| C::Array1D
| C::ArrayMultiD
| C::Map
| C::Structure
| C::Union
| C::Enumeration
| C::Bitmask
| C::Bitset
| C::Alias
| C::Valuetype
| C::ValueBox
| C::Native
| C::Any
| C::ValueBase
| C::ObjectRef
| C::AnnotationType => Verdict::Allowed,
},
}
}
#[must_use]
pub const fn facet_verdict(profile: Profile, property: FacetProperty) -> Verdict {
use FacetProperty as F;
match profile {
Profile::Idl42Core => match property {
F::Key
| F::Optional
| F::External
| F::MustUnderstand
| F::NonSerialized
| F::TryConstruct
| F::NonFinalExtensibility
| F::ExplicitMemberId
| F::DefaultValue
| F::RangeConstraint
| F::Unit
| F::Verbatim => Verdict::Allowed,
F::Ros2DefaultValue => {
idl_no("a per-field default is a ROS 2 construct, spelled @default in IDL")
}
F::VendorDirective => idl_no("a vendor directive is outside IDL 4.2"),
},
Profile::XTypes13 => match property {
F::Key
| F::Optional
| F::External
| F::MustUnderstand
| F::NonSerialized
| F::TryConstruct
| F::NonFinalExtensibility
| F::ExplicitMemberId
| F::DefaultValue
| F::RangeConstraint
| F::Unit
| F::Verbatim => Verdict::Allowed,
F::Ros2DefaultValue => xtypes_no("a ROS 2 default is not an XTypes annotation"),
F::VendorDirective => xtypes_no("a vendor directive is outside XTypes 1.3"),
},
Profile::Corba33 => match property {
F::Verbatim => Verdict::Allowed,
F::Key => corba_no("@key is a DDS concept with no CORBA meaning"),
F::Optional => corba_no("@optional was introduced by XTypes"),
F::External => corba_no("@external was introduced by XTypes"),
F::MustUnderstand => corba_no("@must_understand was introduced by XTypes"),
F::NonSerialized => corba_no("@non_serialized was introduced by XTypes"),
F::TryConstruct => corba_no("@try_construct was introduced by XTypes"),
F::NonFinalExtensibility => {
corba_no("CORBA structs do not evolve; extensibility is an XTypes concept")
}
F::ExplicitMemberId => corba_no("member ids were introduced by XTypes"),
F::DefaultValue => corba_no("@default was introduced by XTypes"),
F::RangeConstraint => corba_no("@range was introduced by XTypes"),
F::Unit => corba_no("@unit was introduced by XTypes"),
F::Ros2DefaultValue => corba_no("a ROS 2 default has no CORBA equivalent"),
F::VendorDirective => corba_no("a vendor directive is outside CORBA 3.3"),
},
Profile::Ros2 => match property {
F::Key | F::Ros2DefaultValue => Verdict::Allowed,
F::Optional => ros_no("ROS 2 fields are always present"),
F::External => ros_no("ROS 2 has no @external"),
F::MustUnderstand => ros_no("ROS 2 has no @must_understand"),
F::NonSerialized => ros_no("ROS 2 has no @non_serialized"),
F::TryConstruct => ros_no("ROS 2 has no @try_construct"),
F::NonFinalExtensibility => {
ros_no("a ROS 2 message is final; evolution goes through a new type")
}
F::ExplicitMemberId => ros_no("ROS 2 fields carry no member id"),
F::DefaultValue => {
ros_no("a ROS 2 default is spelled in the .msg file, not as @default")
}
F::RangeConstraint => ros_no("ROS 2 has no @range"),
F::Unit => ros_no("ROS 2 has no @unit"),
F::Verbatim => ros_no("ROS 2 has no @verbatim"),
F::VendorDirective => ros_no("a vendor directive is outside ROS 2"),
},
Profile::DdsXml => match property {
F::Key
| F::Optional
| F::External
| F::MustUnderstand
| F::NonSerialized
| F::TryConstruct
| F::NonFinalExtensibility
| F::ExplicitMemberId
| F::DefaultValue
| F::RangeConstraint
| F::Unit => Verdict::Allowed,
F::Verbatim => xml_no("DDS-XML has no @verbatim element"),
F::Ros2DefaultValue => xml_no("a ROS 2 default has no DDS-XML element"),
F::VendorDirective => xml_no("a vendor directive is outside DDS-XML 1.0"),
},
Profile::FlatData => match property {
F::Key
| F::MustUnderstand
| F::ExplicitMemberId
| F::DefaultValue
| F::RangeConstraint
| F::Unit
| F::Verbatim
| F::TryConstruct
| F::NonSerialized
| F::Ros2DefaultValue
| F::VendorDirective => Verdict::Allowed,
F::Optional => flat_no("an optional member makes the layout value-dependent"),
F::External => flat_no("@external is indirection, which a loan cannot follow"),
F::NonFinalExtensibility => {
flat_no("a non-final aggregate carries a header, so its size is not fixed")
}
},
Profile::Vendor(_) => match property {
F::Key
| F::Optional
| F::External
| F::MustUnderstand
| F::NonSerialized
| F::TryConstruct
| F::NonFinalExtensibility
| F::ExplicitMemberId
| F::DefaultValue
| F::RangeConstraint
| F::Unit
| F::Verbatim
| F::Ros2DefaultValue
| F::VendorDirective => Verdict::Allowed,
},
}
}
pub const ALL_PROFILES: [Profile; 7] = [
Profile::Idl42Core,
Profile::XTypes13,
Profile::Corba33,
Profile::Ros2,
Profile::DdsXml,
Profile::FlatData,
Profile::Vendor(VendorKind::Rti),
];