#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
use zerodds_ast_core::ScopedName;
use zerodds_wire_probe::dump_rust::dump_hdds;
use zerodds_wire_probe::encode::{ProbeError, model_from_idl};
fn emit(idl: &str, type_name: &str) -> Result<String, ProbeError> {
let ast = model_from_idl(idl, "probe.idl").expect("the model");
let segments: Vec<String> = type_name
.split("::")
.filter(|segment| !segment.is_empty())
.map(std::string::ToString::to_string)
.collect();
let type_ = ast
.find_by_scoped_name(&ScopedName::absolute(segments))
.ok()
.and_then(zerodds_ast::AnyId::as_type)
.expect("the type");
dump_hdds(&ast, type_)
}
#[test]
fn a_structure_reads_each_field_into_its_key() {
let out = emit("@final struct S { long a; double b; };", "S").expect("the emitter");
assert!(
out.contains(r#"zd_object.insert("a".to_string(), serde_json::Value::from(sample.a))"#),
"{out}"
);
assert!(
out.contains(r#"zd_object.insert("b".to_string(), zd_json_f64(sample.b))"#),
"{out}"
);
}
#[test]
fn a_base_member_is_flattened_and_read_through_the_base_field() {
let out = emit(
"@final struct Base { long base_a; };
@final struct S : Base { long derived_a; };",
"S",
)
.expect("the emitter");
assert!(
out.contains(
r#"zd_object.insert("base_a".to_string(), serde_json::Value::from(sample.base.base_a))"#
),
"{out}"
);
assert!(out.contains(r#"zd_object.insert("derived_a".to_string(), serde_json::Value::from(sample.derived_a))"#), "{out}");
}
#[test]
fn a_sequence_becomes_an_array_over_the_vec() {
let out = emit("@final struct S { sequence<long> ids; };", "S").expect("the emitter");
assert!(out.contains("(sample.ids).iter()"), "{out}");
assert!(out.contains("serde_json::Value::Array("), "{out}");
}
#[test]
fn an_optional_member_is_a_key_only_when_it_is_present() {
let out = emit("@final struct S { long a; @optional long b; };", "S").expect("the emitter");
assert!(out.contains("if let Some("), "{out}");
assert!(
out.contains(r#"zd_object.insert("b".to_string(),"#),
"{out}"
);
assert!(
!out.contains(r#"zd_object.insert("b".to_string(), serde_json::Value::Null)"#),
"{out}"
);
}
#[test]
fn an_enumeration_is_printed_as_its_variant_name() {
let out = emit("enum Color { RED, GREEN };", "Color").expect("the emitter");
assert!(out.contains("match &sample"), "{out}");
assert!(out.contains(r#"Color::RED => "RED""#), "{out}");
assert!(out.contains(r#"Color::GREEN => "GREEN""#), "{out}");
}
#[test]
fn a_labelled_union_prints_the_discriminator_and_the_member() {
let out = emit(
"union Choice switch (long) {
case 1: long whole;
case 2: short part;
};",
"Choice",
)
.expect("the emitter");
assert!(out.contains("Choice::Whole("), "{out}");
assert!(out.contains("Choice::Part("), "{out}");
assert!(
out.contains(r#"zd_object.insert("_d".to_string(), serde_json::Value::from(1i64))"#),
"{out}"
);
assert!(
out.contains(r#"zd_object.insert("whole".to_string(),"#),
"{out}"
);
}
#[test]
fn a_defaulted_union_is_refused_because_its_discriminator_is_not_stored() {
let error = emit(
"union Choice switch (long) { case 1: long whole; default: short part; };",
"Choice",
)
.expect_err("a default case has no recoverable discriminator");
assert!(matches!(error, ProbeError::Unfilled { .. }), "{error:?}");
}
#[test]
fn a_map_becomes_an_array_of_key_value_pairs() {
let out = emit("@final struct S { map<long, long> table; };", "S").expect("the emitter");
assert!(out.contains("(sample.table).iter()"), "{out}");
assert!(out.contains("serde_json::Value::Array(vec!["), "{out}");
}
#[test]
fn a_bitset_is_refused_because_its_read_shape_is_unmeasured() {
let error = emit(
"bitset Flags { bitfield<3> low; bitfield<5> high; };
@final struct S { Flags bits; };",
"S",
)
.expect_err("no measured bitfield getter");
assert!(matches!(error, ProbeError::Unfilled { .. }), "{error:?}");
}
#[test]
fn a_recursive_type_is_refused() {
let error = emit(
"module m { struct Node;
@mutable struct Node { long value; @optional Node next; }; };",
"m::Node",
)
.expect_err("hddsgen generates no usable type for a cycle");
assert!(matches!(error, ProbeError::Unsupported { .. }), "{error:?}");
}
#[test]
fn a_long_double_is_refused() {
let error = emit("@final struct S { long double x; };", "S")
.expect_err("no Rust primitive for a long double");
assert!(matches!(error, ProbeError::Unsupported { .. }), "{error:?}");
}