1use std::any::Any;
2use std::collections::BTreeMap;
3use std::fmt;
4use std::path::Path;
5use std::sync::Arc;
6
7use runx_contracts::{AuthorityVerb, JsonObject, Receipt, Reference};
8use runx_core::state_machine::AuthorityAdmissionWitness;
9use runx_parser::GraphStep;
10
11use crate::adapter::SkillOutput;
12
13use super::RuntimeEffectError;
14
15pub trait RuntimeEffect: Send + Sync {
16 fn family(&self) -> &'static str;
17
18 fn can_run_parallel(&self, step: &GraphStep) -> bool {
19 let _ = step;
20 true
21 }
22
23 fn find_replay(
24 &self,
25 request: EffectStepRequest<'_>,
26 ) -> Result<Option<EffectReplay>, RuntimeEffectError> {
27 let _ = request;
28 Ok(None)
29 }
30
31 fn recover_pending(&self, request: EffectStepRequest<'_>) -> Result<(), RuntimeEffectError> {
32 let _ = request;
33 Ok(())
34 }
35
36 fn admit(
37 &self,
38 request: EffectStepRequest<'_>,
39 ) -> Result<Option<EffectAdmission>, RuntimeEffectError> {
40 let _ = request;
41 Ok(None)
42 }
43
44 fn prepare_output(&self, request: EffectOutputRequest<'_>) -> Result<(), RuntimeEffectError> {
45 let _ = request;
46 Ok(())
47 }
48
49 fn finalize_output(&self, request: EffectReceiptRequest<'_>) -> Result<(), RuntimeEffectError> {
50 let _ = request;
51 Ok(())
52 }
53
54 fn persist(&self, request: EffectReceiptRequest<'_>) -> Result<(), RuntimeEffectError> {
55 let _ = request;
56 Ok(())
57 }
58
59 fn prepare_replay_output(
60 &self,
61 request: EffectReplayOutputRequest<'_>,
62 ) -> Result<(), RuntimeEffectError> {
63 let _ = request;
64 Ok(())
65 }
66
67 fn validate_replay(
68 &self,
69 request: EffectReplayReceiptRequest<'_>,
70 ) -> Result<(), RuntimeEffectError> {
71 let _ = request;
72 Ok(())
73 }
74
75 fn refresh_output_metadata(
76 &self,
77 request: EffectMetadataRefreshRequest<'_>,
78 ) -> Result<(), RuntimeEffectError> {
79 let _ = request;
80 Ok(())
81 }
82
83 fn authority_grant_refs(
84 &self,
85 admission: &EffectAdmission,
86 ) -> Result<Vec<Reference>, RuntimeEffectError> {
87 let _ = admission;
88 Ok(Vec::new())
89 }
90
91 fn replay_authority_grant_refs(
92 &self,
93 replay: &EffectReplay,
94 ) -> Result<Vec<Reference>, RuntimeEffectError> {
95 let _ = replay;
96 Ok(Vec::new())
97 }
98}
99
100#[derive(Clone, Copy, Debug)]
101pub struct EffectStepRequest<'a> {
102 pub step: &'a GraphStep,
103 pub inputs: &'a JsonObject,
104 pub env: &'a BTreeMap<String, String>,
105 pub graph_dir: &'a Path,
106}
107
108pub struct EffectOutputRequest<'a> {
109 pub step: &'a GraphStep,
110 pub admission: &'a EffectAdmission,
111 pub claim: &'a JsonObject,
112 pub output: &'a mut SkillOutput,
113}
114
115pub struct EffectReceiptRequest<'a> {
116 pub step: &'a GraphStep,
117 pub graph_dir: &'a Path,
118 pub admission: &'a EffectAdmission,
119 pub claim: &'a JsonObject,
120 pub output: &'a mut SkillOutput,
121 pub receipt: &'a Receipt,
122 pub env: &'a BTreeMap<String, String>,
123}
124
125pub struct EffectReplayOutputRequest<'a> {
126 pub step: &'a GraphStep,
127 pub replay: &'a EffectReplay,
128 pub output: &'a mut SkillOutput,
129}
130
131pub struct EffectReplayReceiptRequest<'a> {
132 pub step: &'a GraphStep,
133 pub replay: &'a EffectReplay,
134 pub receipt: &'a Receipt,
135 pub output: &'a SkillOutput,
136 pub claim: &'a JsonObject,
137}
138
139pub struct EffectMetadataRefreshRequest<'a> {
140 pub output: &'a mut SkillOutput,
141 pub receipt: &'a Receipt,
142}
143
144#[derive(Clone)]
145pub struct EffectAdmission {
146 family: &'static str,
147 verb: AuthorityVerb,
148 witness: AuthorityAdmissionWitness,
149 context: Arc<dyn Any + Send + Sync>,
150}
151
152impl EffectAdmission {
153 #[must_use]
154 pub fn new<T>(
155 family: &'static str,
156 verb: AuthorityVerb,
157 witness: AuthorityAdmissionWitness,
158 context: T,
159 ) -> Self
160 where
161 T: Any + Send + Sync + 'static,
162 {
163 Self {
164 family,
165 verb,
166 witness,
167 context: Arc::new(context),
168 }
169 }
170
171 #[must_use]
172 pub fn family(&self) -> &'static str {
173 self.family
174 }
175
176 #[must_use]
177 pub fn verb(&self) -> AuthorityVerb {
178 self.verb.clone()
179 }
180
181 #[must_use]
182 pub fn witness(&self) -> &AuthorityAdmissionWitness {
183 &self.witness
184 }
185
186 #[must_use]
187 pub fn context<T: Any>(&self) -> Option<&T> {
188 self.context.as_ref().downcast_ref::<T>()
189 }
190}
191
192impl fmt::Debug for EffectAdmission {
193 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
194 formatter
195 .debug_struct("EffectAdmission")
196 .field("family", &self.family)
197 .field("verb", &self.verb)
198 .field("witness", &self.witness)
199 .finish_non_exhaustive()
200 }
201}
202
203#[derive(Clone)]
204pub struct EffectReplay {
205 family: &'static str,
206 receipt_ref: String,
207 receipt_created_at: String,
208 receipt_digest: String,
209 outputs: JsonObject,
210 context: Arc<dyn Any + Send + Sync>,
211}
212
213impl EffectReplay {
214 #[must_use]
215 pub fn new<T>(
216 family: &'static str,
217 receipt_ref: impl Into<String>,
218 receipt_created_at: impl Into<String>,
219 receipt_digest: impl Into<String>,
220 outputs: JsonObject,
221 context: T,
222 ) -> Self
223 where
224 T: Any + Send + Sync + 'static,
225 {
226 Self {
227 family,
228 receipt_ref: receipt_ref.into(),
229 receipt_created_at: receipt_created_at.into(),
230 receipt_digest: receipt_digest.into(),
231 outputs,
232 context: Arc::new(context),
233 }
234 }
235
236 #[must_use]
237 pub fn family(&self) -> &'static str {
238 self.family
239 }
240
241 #[must_use]
242 pub fn receipt_ref(&self) -> &str {
243 &self.receipt_ref
244 }
245
246 #[must_use]
247 pub fn receipt_created_at(&self) -> &str {
248 &self.receipt_created_at
249 }
250
251 #[must_use]
252 pub fn receipt_digest(&self) -> &str {
253 &self.receipt_digest
254 }
255
256 #[must_use]
257 pub fn outputs(&self) -> &JsonObject {
258 &self.outputs
259 }
260
261 #[must_use]
262 pub fn context<T: Any>(&self) -> Option<&T> {
263 self.context.as_ref().downcast_ref::<T>()
264 }
265}
266
267impl fmt::Debug for EffectReplay {
268 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
269 formatter
270 .debug_struct("EffectReplay")
271 .field("family", &self.family)
272 .field("receipt_ref", &self.receipt_ref)
273 .field("receipt_created_at", &self.receipt_created_at)
274 .field("receipt_digest", &self.receipt_digest)
275 .finish_non_exhaustive()
276 }
277}