Skip to main content

zerodds_wire_probe/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Crate `zerodds-wire-probe`. Safety classification: **SAFE**.
4//!
5//! One IDL type plus one JSON value, in; the bytes that type takes on
6//! the wire, out. This is the ZeroDDS side of the interop suite —
7//! `docs/specs/zerodds-interop-suite-1.0.md`.
8//!
9//! ## Why it is reflective and not generated
10//!
11//! A probe that went through the code generator would test the
12//! generator. This one walks the model and the layout plan directly, so
13//! a difference between it and a foreign implementation is a difference
14//! in what the plan says, which is the thing under test. The generated
15//! path is tested separately, by `zerodds-wire-conformance`.
16//!
17//! ## The value format
18//!
19//! One JSON document per sample, read against the type rather than
20//! against a Rust struct, so an unknown field or a missing member is an
21//! error naming the path rather than a silent default.
22//!
23//! | IDL | JSON |
24//! |---|---|
25//! | `boolean` | `true` / `false` |
26//! | integers up to 32 bits | number |
27//! | `long long`, `unsigned long long` | **string** — a JSON number loses precision past 2^53 |
28//! | `float`, `double`, `long double` | number, written round-trip exact |
29//! | `char`, `wchar` | one-character string |
30//! | `string`, `wstring` | string |
31//! | `fixed<P, S>` | **string** — a decimal, `"123.45"`, because a JSON number is a double |
32//! | `enum` | the enumerator's name, as a string |
33//! | `bitmask` | array of the set bit names |
34//! | `bitset` | object, keyed by bitfield name, each an integer |
35//! | structure | object, keyed by member name |
36//! | `sequence`, array | array |
37//! | `map<K, V>` | **array of two-element arrays** — a JSON object keys on a string, and `K` need not be one |
38//! | `union` | object with `_d` and the selected member |
39//! | `@optional` absent | the key is left out — `null` is an error |
40//!
41//! ## Layer position
42//!
43//! A tool over `zerodds-wire-codec`, which is the plan executor itself
44//! and knows nothing about JSON. What is here is the probe: the value
45//! file's representation (`json`), the front end that turns IDL text
46//! into a model, and the five foreign-column emitters — C for Cyclone
47//! and RTI, C++ for Fast DDS, C for the ROS type supports, and the three
48//! CORBA columns.
49//!
50//! The engine modules below — [`canonical`], [`contract`], [`mod@decode`],
51//! [`default`], [`mod@encode`], [`value`] — are that crate's, pinned to
52//! `serde_json::Value`. Nothing is re-derived here: the wrappers forward
53//! and the signatures are the ones every call site already had.
54#![forbid(unsafe_code)]
55
56/// The corpus cases the IDL front end refuses, so that no walk over
57/// `tests/interop-corpus/` gets a model for them.
58///
59/// Both are in the corpus deliberately, and both have a README saying
60/// every implementation must refuse the file. What changed is *where*
61/// the refusal happens: it used to arrive at the layout plan or at the
62/// value contract, with a model in hand; `zerodds_idl_compose` carries
63/// the two clauses now, so `model_from_idl` answers `Err` and there is
64/// no model to walk.
65///
66/// - `recursive-array` — `struct Node { long value; Node pair[2]; }`.
67///   A cycle with nothing on it that terminates a value: an array's
68///   element count is fixed and non-zero, so no value of the type is
69///   finite. IDL 4.2 §7.4.1.4.4.4.4.
70/// - `resolve-module-named-as-type` — `module common { struct common
71///   { … }; }`. IDL 4.2 §7.5.2 does not admit a name redefined within
72///   the immediate scope of the construct that carries it, and prints
73///   `module M { typedef short M; }` as its own `// Error` example. The
74///   case measures which vendors refuse the file and which resolve it
75///   anyway.
76///
77/// `tools/idlc/tests/corpus.rs` carries both as `Intended` diagnostics,
78/// with the same two clauses.
79///
80/// A list and never a pattern: a case that stops being refused is as
81/// much a finding as one that starts, and each walk that reads this
82/// asserts the refusal it skips actually happened.
83pub const REFUSED_BY_THE_FRONT_END: [&str; 2] = ["recursive-array", "resolve-module-named-as-type"];
84
85pub mod canonical;
86pub mod contract;
87pub mod corba_cpp;
88pub mod corba_idl;
89pub mod corba_java;
90pub mod decode;
91pub mod default;
92pub mod dump;
93pub mod dump_cpp;
94pub mod dump_ros;
95pub mod dump_rust;
96pub mod encode;
97pub mod fill;
98pub mod fill_cpp;
99pub mod fill_ros;
100pub mod fill_rust;
101pub mod matrix;
102pub mod recursion;
103pub mod ros_idl;
104pub mod target_keywords;
105pub mod value;
106
107pub use canonical::{Difference, canonicalise};
108pub use decode::decode;
109pub use default::of_type as default_of_type;
110pub use dump::dump;
111pub use dump_cpp::dump_fastdds;
112pub use dump_rust::dump_hdds;
113pub use encode::{ProbeError, encode};
114pub use fill::{Vendor, fill};
115pub use value::{Emit, ProbeRequest};
116pub use zerodds_wire_codec::json::EXACT_JSON_INTEGER;