extern crate alloc;
use alloc::boxed::Box;
use alloc::string::String;
use super::builder::DynamicTypeBuilder;
use super::descriptor::{MemberDescriptor, TypeDescriptor, TypeKind};
use super::error::DynamicError;
use super::type_::DynamicType;
pub const NAME_DDS_STRING: &str = "DDS::String";
pub const NAME_DDS_KEYED_STRING: &str = "DDS::KeyedString";
pub const NAME_DDS_BYTES: &str = "DDS::Bytes";
pub const NAME_DDS_KEYED_BYTES: &str = "DDS::KeyedBytes";
pub fn dds_string() -> Result<DynamicType, DynamicError> {
let mut desc = TypeDescriptor::structure(NAME_DDS_STRING);
desc.is_nested = true;
let mut builder = DynamicTypeBuilder::new(desc);
builder.add_member(MemberDescriptor::new("value", 1, unbounded_string()))?;
builder.build()
}
fn unbounded_string() -> TypeDescriptor {
let mut t = TypeDescriptor::primitive(TypeKind::String8, "string");
t.bound = alloc::vec![0];
t
}
fn unbounded_octet_sequence() -> TypeDescriptor {
let mut t = TypeDescriptor::primitive(TypeKind::Sequence, "sequence");
t.bound = alloc::vec![0];
t.element_type = Some(Box::new(TypeDescriptor::primitive(TypeKind::Byte, "octet")));
t
}
pub fn dds_keyed_string() -> Result<DynamicType, DynamicError> {
let mut desc = TypeDescriptor::structure(NAME_DDS_KEYED_STRING);
desc.is_nested = true;
let mut builder = DynamicTypeBuilder::new(desc);
let mut key = MemberDescriptor::new("key", 1, unbounded_string());
key.is_key = true;
builder.add_member(key)?;
builder.add_member(MemberDescriptor::new("value", 2, unbounded_string()))?;
builder.build()
}
pub fn dds_bytes() -> Result<DynamicType, DynamicError> {
let mut desc = TypeDescriptor::structure(NAME_DDS_BYTES);
desc.is_nested = true;
let mut builder = DynamicTypeBuilder::new(desc);
builder.add_member(MemberDescriptor::new(
"value",
1,
unbounded_octet_sequence(),
))?;
builder.build()
}
pub fn dds_keyed_bytes() -> Result<DynamicType, DynamicError> {
let mut desc = TypeDescriptor::structure(NAME_DDS_KEYED_BYTES);
desc.is_nested = true;
let mut builder = DynamicTypeBuilder::new(desc);
let mut key = MemberDescriptor::new("key", 1, unbounded_string());
key.is_key = true;
builder.add_member(key)?;
builder.add_member(MemberDescriptor::new(
"value",
2,
unbounded_octet_sequence(),
))?;
builder.build()
}
pub fn all_builtin_types() -> Result<[(String, DynamicType); 4], DynamicError> {
Ok([
(String::from(NAME_DDS_STRING), dds_string()?),
(String::from(NAME_DDS_KEYED_STRING), dds_keyed_string()?),
(String::from(NAME_DDS_BYTES), dds_bytes()?),
(String::from(NAME_DDS_KEYED_BYTES), dds_keyed_bytes()?),
])
}
#[must_use]
pub fn is_builtin_type_name(name: &str) -> bool {
matches!(
name,
NAME_DDS_STRING | NAME_DDS_KEYED_STRING | NAME_DDS_BYTES | NAME_DDS_KEYED_BYTES
)
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn spec_names_match_annex_e() {
assert_eq!(NAME_DDS_STRING, "DDS::String");
assert_eq!(NAME_DDS_KEYED_STRING, "DDS::KeyedString");
assert_eq!(NAME_DDS_BYTES, "DDS::Bytes");
assert_eq!(NAME_DDS_KEYED_BYTES, "DDS::KeyedBytes");
}
#[test]
fn dds_string_has_one_value_member() {
let t = dds_string().unwrap();
assert_eq!(t.name(), NAME_DDS_STRING);
assert_eq!(t.kind(), TypeKind::Structure);
assert_eq!(t.member_count(), 1);
let member = t.member_by_name("value").unwrap();
assert_eq!(member.dynamic_type().kind(), TypeKind::String8);
assert!(!member.descriptor().is_key);
assert!(t.descriptor().is_nested);
}
#[test]
fn dds_keyed_string_has_key_and_value() {
let t = dds_keyed_string().unwrap();
assert_eq!(t.name(), NAME_DDS_KEYED_STRING);
assert_eq!(t.member_count(), 2);
let key = t.member_by_name("key").unwrap();
assert!(key.descriptor().is_key);
assert_eq!(key.dynamic_type().kind(), TypeKind::String8);
let value = t.member_by_name("value").unwrap();
assert!(!value.descriptor().is_key);
assert_eq!(value.dynamic_type().kind(), TypeKind::String8);
}
#[test]
fn dds_bytes_has_unbounded_octet_sequence() {
let t = dds_bytes().unwrap();
assert_eq!(t.name(), NAME_DDS_BYTES);
assert_eq!(t.member_count(), 1);
let value = t.member_by_name("value").unwrap();
assert_eq!(value.dynamic_type().kind(), TypeKind::Sequence);
assert_eq!(value.dynamic_type().descriptor().bound, alloc::vec![0]);
}
#[test]
fn dds_keyed_bytes_has_key_and_octet_sequence() {
let t = dds_keyed_bytes().unwrap();
assert_eq!(t.name(), NAME_DDS_KEYED_BYTES);
assert_eq!(t.member_count(), 2);
assert!(t.member_by_name("key").unwrap().descriptor().is_key);
assert_eq!(
t.member_by_name("value").unwrap().dynamic_type().kind(),
TypeKind::Sequence
);
}
#[test]
fn all_builtin_types_returns_four_with_correct_names() {
let types = all_builtin_types().unwrap();
let names: alloc::vec::Vec<_> = types.iter().map(|(n, _)| n.as_str()).collect();
assert_eq!(
names,
[
"DDS::String",
"DDS::KeyedString",
"DDS::Bytes",
"DDS::KeyedBytes"
]
);
}
#[test]
fn is_builtin_type_name_recognises_all_four() {
assert!(is_builtin_type_name("DDS::String"));
assert!(is_builtin_type_name("DDS::KeyedString"));
assert!(is_builtin_type_name("DDS::Bytes"));
assert!(is_builtin_type_name("DDS::KeyedBytes"));
assert!(!is_builtin_type_name("dds::string"));
assert!(!is_builtin_type_name("MyApp::Custom"));
}
#[test]
fn all_builtin_types_have_nested_annotation() {
for (name, t) in all_builtin_types().unwrap() {
assert!(
t.descriptor().is_nested,
"{name} should have is_nested=true"
);
}
}
#[test]
fn dds_string_singleton_calls_produce_equal_types() {
let a = dds_string().unwrap();
let b = dds_string().unwrap();
assert!(a.equals(&b));
}
#[test]
fn keyed_types_have_exactly_one_key_member() {
for (name, t) in [
(NAME_DDS_KEYED_STRING, dds_keyed_string().unwrap()),
(NAME_DDS_KEYED_BYTES, dds_keyed_bytes().unwrap()),
] {
let key_count = (0..t.member_count())
.filter_map(|i| t.member_by_index(i))
.filter(|m| m.descriptor().is_key)
.count();
assert_eq!(key_count, 1, "{name}: expected exactly 1 key member");
}
}
#[test]
fn unkeyed_types_have_no_key_member() {
for (name, t) in [
(NAME_DDS_STRING, dds_string().unwrap()),
(NAME_DDS_BYTES, dds_bytes().unwrap()),
] {
let key_count = (0..t.member_count())
.filter_map(|i| t.member_by_index(i))
.filter(|m| m.descriptor().is_key)
.count();
assert_eq!(key_count, 0, "{name}: expected no key members");
}
}
}