Skip to main content

scientific_workflow/
system_state.rs

1//! Template-defined, heterogeneous scientific system states.
2//!
3//! This module is the complete public boundary for describing one scientific
4//! system at a particular time point. A program first loads a JSON template
5//! into [`SystemStateSchema`], constructs its initial blank [`SystemState`], and then
6//! moves concrete Rust payloads into and out of the declared fields.
7//!
8//! # Public workflow
9//!
10//! 1. Load and validate a template with [`SystemStateSchema::load_json_template`].
11//! 2. Create the initial state with [`SystemStateSchema::create_empty_state`].
12//! 3. Assemble payload types and owners with [`SystemState::insert_payload`].
13//! 4. Borrow, mutate, or extract payloads through [`SystemState`].
14//!    Coordinated kernels use [`SystemState::borrow_payloads`] or
15//!    [`SystemState::borrow_payloads_mut`] with matching type and field-name tuples.
16//! 5. Mutate time through [`SystemState::replace_simulation_time`] or
17//!    [`SystemState::advance_simulation_time`].
18//! 6. Create later blank states with
19//!    [`SystemState::clone_structure_without_payloads`].
20//!
21//! The template fixes field names, field order, and optional human-facing
22//! descriptions. It contains no Rust type or storage codec information.
23//! Individual payload slots may be empty, but callers cannot add, remove, or
24//! reorder fields after the template is loaded. First insertion binds a slot's
25//! concrete Rust type. That contract survives extraction and clearing and is
26//! inherited by blank states derived from an assembled instance.
27//!
28//! Every inserted payload implements Serde `Serialize`, `Clone`, `Send`, and
29//! `'static`. Serialization is supplied by the payload type itself; this
30//! module only retains a private borrowed erased view for the future storage
31//! encoder. It does not select JSON framing or perform IO.
32//!
33//! # Ownership
34//!
35//! [`SystemState::insert_payload`] consumes a concrete payload without cloning it. An
36//! insertion into an empty slot returns `None`; replacement returns the
37//! previous payload as `Some(T)`, preserving its ownership instead of dropping
38//! it. A rejected insertion returns [`PayloadInsertError<T>`], from which the unchanged
39//! incoming payload can be recovered.
40//!
41//! [`SystemState::take_payload`] moves a stored payload back to the caller. Together,
42//! insertion and extraction allow large scientific allocations to cross the state
43//! boundary without copying their contents. Explicitly cloning a
44//! [`SystemState`] is intentionally different: it creates a new erased box and
45//! invokes each populated payload's `Clone` implementation. Clone depth is
46//! therefore defined by the concrete payload type.
47//!
48//! The public insertion contract deliberately makes replacement visible:
49//!
50//! ```no_run
51//! use scientific_workflow::system_state::{SystemStateSchema, SimulationTime};
52//!
53//! # fn example(spec: &SystemStateSchema) -> Result<(), Box<dyn std::error::Error>> {
54//! let mut state = spec.create_empty_state(SimulationTime::from_iteration(0));
55//!
56//! let previous = state.insert_payload("population", vec![1_u64, 2, 3])?;
57//! assert!(previous.is_none());
58//!
59//! let previous = state.insert_payload("population", vec![4_u64, 5, 6])?;
60//! assert_eq!(previous, Some(vec![1, 2, 3]));
61//!
62//! let time = state.advance_simulation_time(None)?;
63//! assert_eq!(time.iteration(), 1);
64//! # Ok(())
65//! # }
66//! ```
67//!
68//! Ignoring a successful replacement result would drop the displaced payload.
69//! Callers should bind or explicitly drop the returned `Option<T>` so that
70//! ownership disposal is intentional.
71//!
72//! # Encapsulation
73//!
74//! Runtime type erasure and boxing are private implementation details.
75//! Downstream crates interact only with concrete types through generic state
76//! methods. Template parsing representations, compact field indices, and
77//! name-to-slot lookup tables are likewise hidden behind the public types
78//! re-exported below.
79//!
80//! Type erasure remains limited to the private heterogeneous owner. Concrete
81//! payload types and runtime identities are retained, and serialization
82//! erasure is borrowed only when storage explicitly requests it.
83
84mod error;
85mod schema;
86mod state;
87mod value;
88
89pub use error::{PayloadInsertError, StateError};
90pub use schema::{StateFieldSchema, SystemStateSchema};
91#[doc(hidden)]
92pub use state::PayloadTuple;
93pub use state::{SimulationTime, SystemState};