pub trait StateTyping:
Send
+ Sync
+ 'static {
type Data;
// Required methods
fn construct_data(
&self,
state: &WorkflowState,
) -> Result<Self::Data, AppError>;
fn construct_state(
&self,
data: &Self::Data,
) -> Result<WorkflowState, AppError>;
// Provided methods
fn validate_pre(
&self,
_state: &WorkflowState,
_change: &StateChange,
) -> Result<(), AppError> { ... }
fn validate_post(
&self,
_before: &WorkflowState,
_after: &WorkflowState,
_change: &StateChange,
) -> Result<(), AppError> { ... }
fn validate_state(&self, _state: &WorkflowState) -> Result<(), AppError> { ... }
fn validate_roundtrip(&self, state: &WorkflowState) -> Result<(), AppError> { ... }
}Expand description
Caller-owned typed construction and state validation.
Data is created and consumed locally and need not be Clone, Send or serializable. The default roundtrip checks successful reconstruction, not raw equality.
§Example
use app_kit::{AppError, workflow::{StateTyping, StateChange, WorkflowState}};
use serde_json::{Map, Value};
struct Counter;
impl StateTyping for Counter {
type Data = i64;
fn construct_data(&self, state: &WorkflowState) -> Result<i64, AppError> {
state.values().get("count").and_then(Value::as_i64)
.ok_or_else(|| AppError::InvalidInput("count must be an integer".into()))
}
fn construct_state(&self, count: &i64) -> Result<WorkflowState, AppError> {
WorkflowState::new(Map::from_iter([("count".into(), Value::from(*count))]))
}
fn validate_pre(&self, _state: &WorkflowState, change: &StateChange) -> Result<(), AppError> {
if let StateChange::Increment { values } = change {
if values.iter().any(|(_, delta)| delta.as_i64().is_some_and(|value| value < 0)) {
return Err(AppError::InvalidInput("count cannot decrease".into()));
}
}
Ok(())
}
fn validate_post(&self, before: &WorkflowState, after: &WorkflowState, _change: &StateChange) -> Result<(), AppError> {
if self.construct_data(after)? < self.construct_data(before)? {
return Err(AppError::InvalidInput("count cannot decrease".into()));
}
Ok(())
}
fn validate_state(&self, state: &WorkflowState) -> Result<(), AppError> {
if self.construct_data(state)? < 0 {
return Err(AppError::InvalidInput("count must be nonnegative".into()));
}
Ok(())
}
}
let before = Counter.construct_state(&2)?;
let change = StateChange::Increment { values: vec![("count".into(), Value::from(1))] };
let after = before.apply(&change, &Counter)?;
assert_eq!(Counter.construct_data(&after)?, 3);
assert!(Counter.validate_state(&Counter.construct_state(&-1)?).is_err());Required Associated Types§
Required Methods§
Sourcefn construct_data(&self, state: &WorkflowState) -> Result<Self::Data, AppError>
fn construct_data(&self, state: &WorkflowState) -> Result<Self::Data, AppError>
Constructs caller data from a validated state.
§Errors
Return an error when the state cannot be represented as Self::Data.
Sourcefn construct_state(&self, data: &Self::Data) -> Result<WorkflowState, AppError>
fn construct_state(&self, data: &Self::Data) -> Result<WorkflowState, AppError>
Reconstructs workflow values from caller data.
§Errors
Return an error when caller data cannot produce a valid workflow state.
Provided Methods§
Sourcefn validate_pre(
&self,
_state: &WorkflowState,
_change: &StateChange,
) -> Result<(), AppError>
fn validate_pre( &self, _state: &WorkflowState, _change: &StateChange, ) -> Result<(), AppError>
Validates an operation before any candidate mutation.
§Errors
Return an error to reject the complete primitive operation before candidate mutation. The default accepts it; Merge and Subset do not invoke this method.
Sourcefn validate_post(
&self,
_before: &WorkflowState,
_after: &WorkflowState,
_change: &StateChange,
) -> Result<(), AppError>
fn validate_post( &self, _before: &WorkflowState, _after: &WorkflowState, _change: &StateChange, ) -> Result<(), AppError>
Validates the original and candidate states after the operation.
§Errors
Return an error to reject the complete candidate primitive transition. The original state remains unchanged. The default accepts it; Merge and Subset do not invoke this method.
Sourcefn validate_state(&self, _state: &WorkflowState) -> Result<(), AppError>
fn validate_state(&self, _state: &WorkflowState) -> Result<(), AppError>
Validates all candidate values after transition validation.
§Errors
Return an error when any full-state invariant is violated. The default accepts the state; this method also runs for reconstructed Merge/Subset snapshots.
Sourcefn validate_roundtrip(&self, state: &WorkflowState) -> Result<(), AppError>
fn validate_roundtrip(&self, state: &WorkflowState) -> Result<(), AppError>
Verifies both caller conversions succeed; a different reconstruction is allowed.
§Errors
The default propagates either conversion error. Successful reconstruction is sufficient; byte-for-byte or value equality is not required.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".