rs_teststand_serde/lib.rs
1//! Serializing a TestStand™ `PropertyObject` tree.
2//!
3//! A property tree is the engine's universal data structure, locals,
4//! parameters, file and station globals, and every step's own settings are all
5//! the same shape. This crate mirrors one into [`PropertyValue`], which serde
6//! can write to any format and read back, and applies a value tree onto a live
7//! property tree.
8//!
9//! ```no_run
10//! use rs_teststand::Engine;
11//! use rs_teststand_serde::PropertyObjectValue;
12//!
13//! let engine = Engine::new()?;
14//! let globals = engine.globals()?;
15//!
16//! // Out as JSON, edited elsewhere, and back in.
17//! let json = serde_json::to_string_pretty(&globals.to_value()?)?;
18//! globals.apply_value(&serde_json::from_str(&json)?)?;
19//! # Ok::<(), Box<dyn std::error::Error>>(())
20//! ```
21//!
22//! This is an **addition to** [`rs_teststand`], not part of it: the binding
23//! mirrors the COM API and nothing else, and a caller that never serializes a
24//! tree carries no serialization framework. That is also why the entry point is
25//! an extension trait, only a type's own crate may give it inherent methods.
26//!
27//! # What the mapping preserves, and what it cannot
28//!
29//! * **Three numeric storages.** The engine matches `Float64`, `Int64` and
30//! `UInt64` strictly, so they are separate variants rather than one number.
31//! * **Array shape.** Elements are stored column-major, so a 10×2 array is
32//! re-nested by computing offsets rather than walking storage order, reading
33//! linearly would silently transpose it.
34//! * **Authored radix.** A value displayed as `0xa` was written in hex, so it
35//! serializes as the string `"0xa"` and parses back to a number.
36//! * **`NAN`, `IND` and `INF`** become `null`: JSON can write none of them, and
37//! inventing an encoding would force every consumer to learn it. An empty
38//! object reference is *not* null, it stays the string `"Nothing"`.
39//!
40//! Plain JSON has one number type, so a value that fits both signed and
41//! unsigned returns as [`PropertyValue::Integer`]. The number is exact; only the
42//! engine's choice of storage is not. See [`PropertyValue`] for the detail.
43#![forbid(unsafe_code)]
44
45pub mod value;
46
47pub use value::{PropertyObjectValue, PropertyValue};