1extern crate self as pasaka;
23
24use std::{
25 collections::HashMap,
26 sync::{LazyLock, Mutex},
27};
28
29mod choice;
30pub use choice::PassageHandle;
31pub use choice::PassageResult;
32
33mod engine;
34pub use engine::Engine;
35
36mod runner;
37pub use runner::*;
38
39pub trait PassageImpl: 'static
40where
41 Self::State: serde::Serialize + for<'a> serde::Deserialize<'a>,
42{
43 type State;
44
45 fn run(&self, h: PassageHandle, state: Self::State) -> PassageResult;
46
47 fn with_state(&self, state: Self::State) -> Passage {
48 Passage {
49 state: serde_json::to_value(state).unwrap(),
50 registry_key: format!("{}::{}", self.module_path(), self.name()),
51 }
52 }
53
54 fn name(&self) -> &'static str;
55
56 fn module_path(&self) -> &'static str;
57}
58
59#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
63pub struct Passage {
64 state: serde_json::Value,
65 registry_key: String,
66}
67
68impl Passage {
69 pub fn run(self, h: PassageHandle) -> PassageResult {
70 let guard = PASSAGE_REGISTRY
71 .lock()
72 .expect("accessing passage registry should not panic");
73 let f = guard.get(&self.registry_key).expect(&format!(
74 "passage {} should be registered using #[passage]",
75 self.registry_key
76 ));
77
78 f(h, self.state)
79 }
80
81 pub fn state<S: for<'a> serde::Deserialize<'a>>(&self) -> Option<S> {
82 serde_json::from_value(self.state.clone()).ok()
83 }
84}
85
86type BoxedPassage = Box<dyn Fn(PassageHandle, serde_json::Value) -> PassageResult + Send + Sync>;
87
88static PASSAGE_REGISTRY: LazyLock<Mutex<HashMap<String, BoxedPassage>>> =
89 LazyLock::new(|| Mutex::new(HashMap::new()));
90
91pub fn register_passage<P: PassageImpl + Send + Sync>(passage: P) {
95 PASSAGE_REGISTRY
96 .lock()
97 .expect("registering passage registry should not panic")
98 .insert(
99 format!("{}::{}", passage.module_path(), passage.name()),
100 Box::new(move |h, value| {
101 let state: P::State = serde_json::from_value(value)
102 .expect("deserialized value should match passage state");
103 passage.run(h, state)
104 }),
105 );
106}
107
108pub use pasaka_macro::passage;
109
110pub mod macro_support {
112 pub mod ctor {
114 pub use ctor::*;
115 }
116
117 pub mod serde {
119 pub use serde::*;
120 }
121
122 #[cfg(feature = "web")]
124 pub mod yew {
125 pub use yew::*;
126 }
127}