use std::any::Any;
use std::any::TypeId;
use std::any::type_name;
use std::convert::Infallible;
use std::io::Error;
use std::rc::Rc;
use qubit_codec::ValueCodecExecutionError;
use qubit_codec::ValueDecoder;
use qubit_codec::ValueEncoder;
use qubit_codec::ValueStringCodecDescriptor;
#[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> {
if *input == u32::MAX {
Err(Error::other("mirror string encode rejected u32::MAX"))
} else {
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("mirror string decode needs a decimal number"))
}
}
#[derive(Default)]
struct MirrorTextStringCodec;
impl ValueEncoder<String> for MirrorTextStringCodec {
type Output = String;
type Error = Error;
fn encode(&mut self, input: &String) -> Result<Self::Output, Self::Error> {
Ok(input.clone())
}
}
#[derive(Default)]
struct FreshStringCodec {
calls: usize,
}
impl ValueEncoder<String> for FreshStringCodec {
type Output = String;
type Error = Error;
fn encode(&mut self, input: &String) -> Result<String, Error> {
self.calls += 1;
Ok(format!("{}:{input}", self.calls))
}
}
impl ValueDecoder<str> for FreshStringCodec {
type Output = String;
type Error = Error;
fn decode(&mut self, input: &str) -> Result<String, Error> {
self.calls += 1;
Ok(format!("{}:{input}", self.calls))
}
}
impl ValueDecoder<str> for MirrorTextStringCodec {
type Output = String;
type Error = Error;
fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
Ok(input.to_owned())
}
}
static U32_DESCRIPTOR: ValueStringCodecDescriptor = ValueStringCodecDescriptor::of::<MirrorU32StringCodec, u32>();
static TEXT_DESCRIPTOR: ValueStringCodecDescriptor = ValueStringCodecDescriptor::of::<MirrorTextStringCodec, String>();
static FRESH_STRING: ValueStringCodecDescriptor = ValueStringCodecDescriptor::of::<FreshStringCodec, String>();
#[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> {
if input == "reject" {
return Err(Error::other("review decode rejected"));
}
Ok(input.as_bytes().to_vec())
}
}
#[derive(Default)]
struct ReviewBorrowedTextCodec;
impl ValueEncoder<str> for ReviewBorrowedTextCodec {
type Output = String;
type Error = Error;
fn encode(&mut self, input: &str) -> Result<String, Error> {
Ok(input.to_owned())
}
}
impl ValueDecoder<str> for ReviewBorrowedTextCodec {
type Output = String;
type Error = Error;
fn decode(&mut self, input: &str) -> Result<String, Error> {
Ok(input.to_owned())
}
}
static REVIEW_BORROWED: ValueStringCodecDescriptor =
ValueStringCodecDescriptor::of_borrowed::<ReviewBorrowedCodec, Vec<u8>, [u8]>();
#[derive(Default)]
struct LocalValueCodec {
_local: Rc<()>,
}
impl ValueEncoder<Rc<u32>> for LocalValueCodec {
type Output = String;
type Error = Infallible;
fn encode(&mut self, input: &Rc<u32>) -> Result<String, Infallible> {
Ok(input.to_string())
}
}
impl ValueDecoder<str> for LocalValueCodec {
type Output = Rc<u32>;
type Error = Infallible;
fn decode(&mut self, _input: &str) -> Result<Rc<u32>, Infallible> {
Ok(Rc::new(7))
}
}
#[test]
fn test_metadata_matches_the_types_selected_at_construction() {
assert_eq!(U32_DESCRIPTOR.codec_type_id(), TypeId::of::<MirrorU32StringCodec>());
assert_eq!(
U32_DESCRIPTOR.codec_type_name(),
type_name::<MirrorU32StringCodec>(),
"the codec name must be diagnostic metadata, not a stable ID"
);
assert_eq!(U32_DESCRIPTOR.value_type_id(), TypeId::of::<u32>());
assert_eq!(
U32_DESCRIPTOR.value_type_name(),
"u32",
"a primitive value type must report its plain diagnostic name"
);
}
#[test]
fn test_debug_output_lists_both_names_and_is_non_exhaustive() {
let rendered = format!("{U32_DESCRIPTOR:?}");
for fragment in [
"ValueStringCodecDescriptor",
"codec_type_name",
type_name::<MirrorU32StringCodec>(),
"value_type_name",
"u32",
"..",
] {
assert!(
rendered.contains(fragment),
"the Debug output must contain {fragment:?}, but rendered {rendered:?}"
);
}
}
#[test]
fn test_type_mismatch_reports_the_declared_and_actual_types() {
let error = U32_DESCRIPTOR
.encode(&42_u64)
.expect_err("a `u64` must not be accepted by a `u32` descriptor");
match error {
ValueCodecExecutionError::TypeMismatch {
expected_type,
actual_type,
} => {
assert_eq!(
expected_type,
type_name::<u32>(),
"the mismatch must name the value type declared by the descriptor"
);
assert_eq!(
actual_type,
TypeId::of::<u64>(),
"the mismatch must report the process-local identity of the value supplied"
);
}
other => panic!("expected a TypeMismatch error, got {other:?}"),
}
}
#[test]
fn test_encode_failure_names_the_codec_and_keeps_the_source_error() {
let error = U32_DESCRIPTOR
.encode(&u32::MAX)
.expect_err("the fixture codec must reject u32::MAX");
match error {
ValueCodecExecutionError::EncodeFailed { codec_type, source } => {
assert_eq!(
codec_type,
type_name::<MirrorU32StringCodec>(),
"an encode failure must name the concrete codec that failed"
);
assert_eq!(
source.to_string(),
"mirror string encode rejected u32::MAX",
"the typed encoder error must remain reachable through the erasure layer"
);
}
other => panic!("expected an EncodeFailed error, got {other:?}"),
}
}
#[test]
fn test_decode_failure_names_the_codec_and_keeps_the_source_error() {
let error = U32_DESCRIPTOR
.decode("not-a-number")
.expect_err("the fixture codec must reject non-numeric text");
match error {
ValueCodecExecutionError::DecodeFailed { codec_type, source } => {
assert_eq!(
codec_type,
type_name::<MirrorU32StringCodec>(),
"a decode failure must name the concrete codec that failed"
);
assert_eq!(
source.to_string(),
"mirror string decode needs a decimal number",
"the typed decoder error must survive erasure"
);
}
other => panic!("expected a DecodeFailed error, got {other:?}"),
}
}
#[test]
fn test_decoded_output_owns_its_data_and_outlives_the_input_buffer() {
let decoded = {
let input = String::from("mirror-owned-text");
TEXT_DESCRIPTOR.decode(&input).expect("any text decodes into a String")
};
let owned = decoded
.downcast_ref::<String>()
.expect("the erased output must be downcastable to the declared value type");
assert_eq!(
owned, "mirror-owned-text",
"the decoded box must own its string rather than borrow the input"
);
}
#[test]
fn test_decoded_output_downcasts_to_the_declared_value_type_only() {
let decoded = U32_DESCRIPTOR.decode("42").expect("decimal text decodes into a u32");
assert!(decoded.downcast_ref::<u32>().is_some());
assert!(
decoded.downcast_ref::<i32>().is_none(),
"the erased output must only downcast to the value type the descriptor declared"
);
}
#[test]
fn test_encode_accepts_any_erased_handle_to_the_declared_type() {
let value: Box<dyn Any> = Box::new(42_u32);
let boxed = U32_DESCRIPTOR.encode(&*value).expect("a boxed `u32` is still a `u32`");
let inline = U32_DESCRIPTOR.encode(&42_u32).expect("a stack `u32` is still a `u32`");
assert_eq!(
boxed, inline,
"the erased entry point must behave identically for boxed and inline values"
);
assert_eq!(boxed, "42");
}
#[test]
fn test_each_operation_uses_a_fresh_default_codec() {
let first = TEXT_DESCRIPTOR.encode(&String::from("mirror")).expect("encode");
let second = TEXT_DESCRIPTOR.encode(&String::from("mirror")).expect("encode");
assert_eq!(
first, second,
"the descriptor must keep no state between operations, so encoding twice must agree"
);
assert_eq!(first, "mirror");
}
#[test]
fn test_copying_a_descriptor_preserves_its_behaviour() {
let copied = U32_DESCRIPTOR;
assert_eq!(copied.codec_type_id(), U32_DESCRIPTOR.codec_type_id());
assert_eq!(
copied.encode(&5_u32).expect("encode"),
U32_DESCRIPTOR.encode(&5_u32).expect("encode"),
"a copied descriptor must stay executable"
);
}
#[test]
fn test_encode_decode_round_trip_preserves_the_value() {
let value = u32::from(u16::MAX);
let encoded = U32_DESCRIPTOR.encode(&value).expect("encode");
let decoded = U32_DESCRIPTOR
.decode(&encoded)
.expect("decode the text just produced")
.downcast_ref::<u32>()
.expect("the erased output must be the declared value type")
.to_owned();
assert_eq!(decoded, value, "a round trip must preserve the value");
}
#[test]
fn test_borrowed_descriptor_round_trip_preserves_owned_values() {
let value = b"hello".to_vec();
assert_eq!(REVIEW_BORROWED.value_type_id(), TypeId::of::<Vec<u8>>());
let text = REVIEW_BORROWED.encode(&value).unwrap();
assert_eq!(text, "hello");
assert_eq!(
*REVIEW_BORROWED.decode(&text).unwrap().downcast::<Vec<u8>>().unwrap(),
value
);
let descriptor = ValueStringCodecDescriptor::of_borrowed::<ReviewBorrowedTextCodec, String, str>();
assert_eq!(descriptor.encode(&String::from("text")).unwrap(), "text");
assert_eq!(
*descriptor.decode("text").unwrap().downcast::<String>().unwrap(),
"text"
);
}
#[test]
fn test_borrowed_descriptor_preserves_type_mismatch_and_source_errors() {
assert!(matches!(REVIEW_BORROWED.encode(&String::from("wrong")),
Err(ValueCodecExecutionError::TypeMismatch { actual_type, .. })
if actual_type == TypeId::of::<String>()));
match REVIEW_BORROWED.encode(&vec![0xff_u8]).unwrap_err() {
ValueCodecExecutionError::EncodeFailed { source, .. } => {
assert!(source.downcast_ref::<Error>().is_some())
}
other => panic!("unexpected error: {other:?}"),
}
match REVIEW_BORROWED.decode("reject").unwrap_err() {
ValueCodecExecutionError::DecodeFailed { source, .. } => {
assert!(source.downcast_ref::<Error>().is_some())
}
other => panic!("unexpected error: {other:?}"),
}
}
#[test]
fn test_decode_of_empty_input_reports_the_codec_failure() {
let error = U32_DESCRIPTOR
.decode("")
.expect_err("empty text is not a decimal number");
assert!(
matches!(error, ValueCodecExecutionError::DecodeFailed { .. }),
"an empty string must surface as a decode failure, got {error:?}"
);
}
#[test]
fn test_each_operation_uses_a_fresh_default_stateful_codec() {
let mut direct = FreshStringCodec::default();
assert_eq!(direct.encode(&String::from("payload")).unwrap(), "1:payload");
assert_eq!(direct.encode(&String::from("payload")).unwrap(), "2:payload");
let payload = String::from("payload");
assert_eq!(FRESH_STRING.encode(&payload).unwrap(), "1:payload");
assert_eq!(FRESH_STRING.encode(&payload).unwrap(), "1:payload");
let decoded = FRESH_STRING.decode("payload").unwrap();
assert_eq!(decoded.downcast_ref::<String>().unwrap(), "1:payload");
let decoded_again = FRESH_STRING.decode("payload").unwrap();
assert_eq!(decoded_again.downcast_ref::<String>().unwrap(), "1:payload");
assert_eq!(FRESH_STRING.encode(&payload).unwrap(), "1:payload");
let decoded_after_encode = FRESH_STRING.decode("payload").unwrap();
assert_eq!(decoded_after_encode.downcast_ref::<String>().unwrap(), "1:payload");
let copied = FRESH_STRING;
assert_eq!(copied.encode(&payload).unwrap(), "1:payload");
let copied_decoded = copied.decode("payload").unwrap();
assert_eq!(copied_decoded.downcast_ref::<String>().unwrap(), "1:payload");
let copied_decoded_again = copied.decode("payload").unwrap();
assert_eq!(copied_decoded_again.downcast_ref::<String>().unwrap(), "1:payload");
}
#[test]
fn test_thread_safe_errors_do_not_require_thread_safe_codec_or_value() {
let descriptor = ValueStringCodecDescriptor::of::<LocalValueCodec, Rc<u32>>();
let value = Rc::new(7_u32);
assert_eq!(descriptor.encode(&value).unwrap(), "7");
let decoded = descriptor.decode("7").unwrap().downcast::<Rc<u32>>().unwrap();
assert_eq!(**decoded, 7);
}