1use std::fmt::Debug;
2use std::future;
3use std::time::SystemTime;
4
5use fedimint_core::core::OperationId;
6use fedimint_core::db::DatabaseTransaction;
7use fedimint_core::encoding::{
8 Decodable, DecodeError, Encodable, decode_field_from_finite_reader,
9 decode_legacy_system_time_from_finite_reader, encode_legacy_system_time, with_decoding_context,
10};
11use fedimint_core::module::registry::ModuleDecoderRegistry;
12use fedimint_core::task::{MaybeSend, MaybeSync};
13use fedimint_core::util::BoxStream;
14use fedimint_core::{apply, async_trait_maybe_send, maybe_add_send, maybe_add_send_sync};
15use futures::{StreamExt, stream};
16use serde::de::DeserializeOwned;
17use serde::{Deserialize, Serialize};
18
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
21#[serde(transparent)]
22pub struct JsonStringed(pub serde_json::Value);
23
24impl Encodable for JsonStringed {
25 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
26 let json_str = serde_json::to_string(&self.0).expect("JSON serialization should not fail");
27 json_str.consensus_encode(writer)
28 }
29}
30
31impl Decodable for JsonStringed {
32 fn consensus_decode_partial<R: std::io::Read>(
33 r: &mut R,
34 modules: &ModuleDecoderRegistry,
35 ) -> Result<Self, DecodeError> {
36 let json_str = String::consensus_decode_partial(r, modules)?;
37 let value = serde_json::from_str(&json_str).map_err(DecodeError::from_err)?;
38 Ok(JsonStringed(value))
39 }
40}
41
42#[apply(async_trait_maybe_send!)]
43pub trait IOperationLog {
44 async fn get_operation(&self, operation_id: OperationId) -> Option<OperationLogEntry>;
45
46 async fn get_operation_dbtx(
47 &self,
48 dbtx: &mut DatabaseTransaction<'_>,
49 operation_id: OperationId,
50 ) -> Option<OperationLogEntry>;
51
52 async fn add_operation_log_entry_dbtx(
53 &self,
54 dbtx: &mut DatabaseTransaction<'_>,
55 operation_id: OperationId,
56 operation_type: &str,
57 operation_meta: serde_json::Value,
58 );
59
60 fn caching_operation_update_stream(
68 &self,
69 operation_id: OperationId,
70 stream_gen: Box<maybe_add_send!(dyn FnOnce() -> BoxStream<'static, serde_json::Value>)>,
71 is_terminal: Box<maybe_add_send_sync!(dyn Fn(&serde_json::Value) -> bool)>,
72 ) -> BoxStream<'static, serde_json::Value>;
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
78pub struct OperationOutcome {
79 pub time: SystemTime,
80 pub outcome: JsonStringed,
81}
82
83impl Encodable for OperationOutcome {
84 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
85 encode_legacy_system_time(&self.time, writer)?;
86 self.outcome.consensus_encode(writer)
87 }
88}
89
90impl Decodable for OperationOutcome {
91 fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
92 reader: &mut R,
93 modules: &ModuleDecoderRegistry,
94 ) -> Result<Self, DecodeError> {
95 Ok(Self {
96 time: with_decoding_context(
97 decode_legacy_system_time_from_finite_reader(reader, modules),
98 "Decoding named block field: OperationOutcome{ ... time ... }",
99 )?,
100 outcome: decode_field_from_finite_reader(
101 reader,
102 modules,
103 "Decoding named block field: OperationOutcome{ ... outcome ... }",
104 )?,
105 })
106 }
107}
108
109#[derive(Debug, Serialize, Deserialize, Encodable, Decodable)]
129pub struct OperationLogEntry {
130 pub(crate) operation_module_kind: String,
131 pub(crate) meta: JsonStringed,
132 pub(crate) outcome: Option<OperationOutcome>,
134}
135
136impl OperationLogEntry {
137 pub fn new(
138 operation_module_kind: String,
139 meta: JsonStringed,
140 outcome: Option<OperationOutcome>,
141 ) -> Self {
142 Self {
143 operation_module_kind,
144 meta,
145 outcome,
146 }
147 }
148
149 pub fn operation_module_kind(&self) -> &str {
151 &self.operation_module_kind
152 }
153
154 pub fn meta<M: DeserializeOwned>(&self) -> M {
160 self.try_meta()
161 .expect("JSON deserialization should not fail")
162 }
163
164 pub fn try_meta<M: DeserializeOwned>(&self) -> Result<M, serde_json::Error> {
168 serde_json::from_value(self.meta.0.clone())
169 }
170
171 pub fn outcome<D: DeserializeOwned>(&self) -> Option<D> {
189 self.try_outcome()
190 .expect("JSON deserialization should not fail")
191 }
192
193 pub fn try_outcome<D: DeserializeOwned>(&self) -> Result<Option<D>, serde_json::Error> {
197 self.outcome
198 .as_ref()
199 .map(|outcome| serde_json::from_value(outcome.outcome.0.clone()))
200 .transpose()
201 }
202
203 pub fn outcome_time(&self) -> Option<SystemTime> {
205 self.outcome.as_ref().map(|o| o.time)
206 }
207
208 pub fn set_outcome(&mut self, outcome: impl Into<Option<OperationOutcome>>) {
209 self.outcome = outcome.into();
210 }
211}
212
213pub enum UpdateStreamOrOutcome<U> {
216 UpdateStream(BoxStream<'static, U>),
217 Outcome(U),
218}
219
220impl<U: Debug> Debug for UpdateStreamOrOutcome<U> {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 match self {
223 UpdateStreamOrOutcome::UpdateStream(_) => write!(f, "UpdateStream"),
224 UpdateStreamOrOutcome::Outcome(o) => f.debug_tuple("Outcome").field(o).finish(),
225 }
226 }
227}
228
229impl<U> UpdateStreamOrOutcome<U>
230where
231 U: MaybeSend + MaybeSync + 'static,
232{
233 pub fn into_stream(self) -> BoxStream<'static, U> {
237 match self {
238 UpdateStreamOrOutcome::UpdateStream(stream) => stream,
239 UpdateStreamOrOutcome::Outcome(outcome) => {
240 Box::pin(stream::once(future::ready(outcome)))
241 }
242 }
243 }
244
245 pub async fn await_outcome(self) -> Option<U> {
249 match self {
250 UpdateStreamOrOutcome::Outcome(outcome) => Some(outcome),
251 UpdateStreamOrOutcome::UpdateStream(mut stream) => {
252 let mut last_update = None;
253 while let Some(update) = stream.next().await {
254 last_update = Some(update);
255 }
256 last_update
257 }
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests;