rust_elm/compose/
reduce_panic.rs1use std::any::Any;
3use std::panic::{catch_unwind, AssertUnwindSafe};
4
5use crate::cmd::Cmd;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct ReducePanic {
10 message: Option<String>,
11}
12
13impl ReducePanic {
14 pub fn message(&self) -> Option<&str> {
15 self.message.as_deref()
16 }
17
18 fn from_payload(payload: Box<dyn Any + Send>) -> Self {
19 let message = payload
20 .downcast_ref::<&str>()
21 .map(|s| (*s).to_string())
22 .or_else(|| payload.downcast_ref::<String>().cloned());
23 Self { message }
24 }
25}
26
27pub fn catch_reduce_panic<S, M, F>(state: &mut S, reduce: F, action: M) -> Result<Cmd<M>, ReducePanic>
30where
31 F: FnOnce(&mut S, M) -> Cmd<M>,
32{
33 match catch_unwind(AssertUnwindSafe(|| reduce(state, action))) {
34 Ok(cmd) => Ok(cmd),
35 Err(payload) => Err(ReducePanic::from_payload(payload)),
36 }
37}
38
39pub fn catch_reduce<S, M, F>(
41 state: &mut S,
42 checkpoint: &mut S,
43 reduce: F,
44 action: M,
45) -> Result<Cmd<M>, ReducePanic>
46where
47 S: Clone,
48 F: FnOnce(&mut S, M) -> Cmd<M>,
49{
50 match catch_unwind(AssertUnwindSafe(|| reduce(state, action))) {
51 Ok(cmd) => {
52 checkpoint.clone_from(state);
53 Ok(cmd)
54 }
55 Err(payload) => {
56 std::mem::swap(state, checkpoint);
57 Err(ReducePanic::from_payload(payload))
58 }
59 }
60}