use std::any::TypeId;
use std::io::Error;
use std::ptr;
use qubit_codec::ValueBytesCodecDescriptor;
use qubit_codec::ValueCodecId;
use qubit_codec::ValueCodecRegistration;
use qubit_codec::ValueCodecRegistrationSource;
use qubit_codec::ValueDecoder;
use qubit_codec::ValueEncoder;
use qubit_codec::ValueStringCodecDescriptor;
use qubit_codec::ValueStringCodecRegistration;
use qubit_codec::ValueStringCodecRegistrationFactory;
use qubit_codec::ValueStringCodecRegistry;
use qubit_codec::register_value_string_codec;
#[derive(Default)]
struct MirrorU32StringCodec;
impl ValueEncoder<u32> for MirrorU32StringCodec {
type Output = String;
type Error = Error;
fn encode(&mut self, input: &u32) -> Result<Self::Output, Self::Error> {
Ok(input.to_string())
}
}
impl ValueDecoder<str> for MirrorU32StringCodec {
type Output = u32;
type Error = Error;
fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
input.parse().map_err(Error::other)
}
}
#[derive(Default)]
struct ReviewBorrowedCodec;
impl ValueEncoder<[u8]> for ReviewBorrowedCodec {
type Output = String;
type Error = Error;
fn encode(&mut self, input: &[u8]) -> Result<String, Error> {
String::from_utf8(input.to_vec()).map_err(Error::other)
}
}
impl ValueDecoder<str> for ReviewBorrowedCodec {
type Output = Vec<u8>;
type Error = Error;
fn decode(&mut self, input: &str) -> Result<Vec<u8>, Error> {
Ok(input.as_bytes().to_vec())
}
}
#[derive(Default)]
struct MirrorU32BytesCodec;
impl ValueEncoder<u32> for MirrorU32BytesCodec {
type Output = Vec<u8>;
type Error = Error;
fn encode(&mut self, input: &u32) -> Result<Self::Output, Self::Error> {
Ok(input.to_be_bytes().to_vec())
}
}
impl ValueDecoder<[u8]> for MirrorU32BytesCodec {
type Output = u32;
type Error = Error;
fn decode(&mut self, input: &[u8]) -> Result<Self::Output, Self::Error> {
let bytes: [u8; 4] = input
.try_into()
.map_err(|_| Error::other("expected exactly four bytes"))?;
Ok(u32::from_be_bytes(bytes))
}
}
static STRING_DESCRIPTOR: ValueStringCodecDescriptor = ValueStringCodecDescriptor::of::<MirrorU32StringCodec, u32>();
static BYTES_DESCRIPTOR: ValueBytesCodecDescriptor = ValueBytesCodecDescriptor::of::<MirrorU32BytesCodec, u32>();
fn build_first() -> ValueStringCodecRegistration {
ValueCodecRegistration::new(
ValueCodecId::new("mirror.factory.string.first"),
&STRING_DESCRIPTOR,
ValueCodecRegistrationSource::new("mirror-crate", "mirror::factory", "src/factory.rs", 17),
)
}
fn build_second() -> ValueStringCodecRegistration {
ValueCodecRegistration::new(
ValueCodecId::new("mirror.factory.string.second"),
&STRING_DESCRIPTOR,
ValueCodecRegistrationSource::new("mirror-crate", "mirror::factory", "src/other.rs", 3),
)
}
register_value_string_codec!(
id = "mirror.factory.string.inventory",
codec = MirrorU32StringCodec,
value = u32,
);
register_value_string_codec!(
id = "review.borrowed.bytes", codec = ReviewBorrowedCodec,
value = Vec<u8>, input = [u8],
);
#[test]
fn test_invoking_the_factory_returns_the_registration_built_by_its_callback() {
let factory = ValueStringCodecRegistrationFactory(build_first);
let registration = (factory.0)();
assert_eq!(
registration.id().as_str(),
"mirror.factory.string.first",
"the factory must yield exactly what its callback produced"
);
assert!(
ptr::eq(registration.descriptor(), &STRING_DESCRIPTOR),
"the built registration must keep borrowing the callback's static descriptor"
);
}
#[test]
fn test_factory_callback_needs_no_arguments_and_no_registry_context() {
let factory = ValueStringCodecRegistrationFactory(build_first);
let callback: fn() -> ValueStringCodecRegistration = factory.0;
let registration = callback();
assert_eq!(
registration.source().line(),
17,
"a zero-argument callback must be callable without any registry state"
);
}
#[test]
fn test_two_invocations_produce_independent_registrations_over_one_descriptor() {
let factory = ValueStringCodecRegistrationFactory(build_first);
let first = (factory.0)();
let second = (factory.0)();
assert_eq!(
first.id(),
second.id(),
"repeated invocation must build the same stable ID"
);
assert!(
ptr::eq(first.descriptor(), second.descriptor()),
"repeated invocation must not construct fresh descriptor state"
);
}
#[test]
fn test_two_factories_serve_different_stable_ids() {
let first = ValueStringCodecRegistrationFactory(build_first);
let second = ValueStringCodecRegistrationFactory(build_second);
assert_ne!(
(first.0)().id(),
(second.0)().id(),
"independent factories must remain independently addressable"
);
}
#[test]
fn test_factory_registration_enters_the_global_registry() {
let registry = ValueStringCodecRegistry::try_global().expect("inventory registrations must freeze");
let found = registry
.get("mirror.factory.string.inventory")
.expect("the inventory factory result must be reachable by its stable ID");
assert_eq!(
found.source().module_path(),
"tests::value::value_string_codec_registration_factory_tests",
"the registry must retain the declaration site of the submitted factory"
);
assert_eq!(
found
.descriptor()
.encode(&11_u32)
.expect("registered string codec should encode"),
"11",
);
}
#[test]
fn test_factory_does_not_restrict_the_declared_wire_representation() {
let factory = ValueStringCodecRegistrationFactory(build_first);
let registration = (factory.0)();
assert_eq!(
registration.descriptor().codec_type_id(),
STRING_DESCRIPTOR.codec_type_id(),
"the factory must keep the codec type the callback declared"
);
assert_ne!(
registration.descriptor().codec_type_id(),
BYTES_DESCRIPTOR.codec_type_id(),
"a string factory must not smuggle in the bytes descriptor"
);
assert_eq!(
registration.descriptor().value_type_id(),
TypeId::of::<u32>(),
"the value type must come from the callback's descriptor"
);
}
#[test]
fn test_factory_built_registration_remains_executable() {
let factory = ValueStringCodecRegistrationFactory(build_first);
let registration = (factory.0)();
assert_eq!(
registration.descriptor().encode(&11_u32).expect("encode"),
"11",
"a factory-built registration must stay executable through its descriptor"
);
}
#[test]
fn test_review_borrowed_registration_executes_through_inventory() {
let registry = ValueStringCodecRegistry::try_global().unwrap();
let descriptor = registry.get("review.borrowed.bytes").unwrap().descriptor();
assert_eq!(descriptor.value_type_id(), TypeId::of::<Vec<u8>>());
assert_eq!(descriptor.encode(&b"hello".to_vec()).unwrap(), "hello");
assert_eq!(
*descriptor.decode("hello").unwrap().downcast::<Vec<u8>>().unwrap(),
b"hello".to_vec()
);
}