1use async_trait::async_trait;
10use iota_interaction::OptionalSync;
11use iota_interaction::rpc_types::{IotaTransactionBlockEffects, IotaTransactionBlockEvents};
12use iota_sdk_types::{Address, ObjectId, ProgrammableTransaction};
13use product_common::core_client::CoreClientReadOnly;
14use product_common::transaction::transaction_builder::Transaction;
15use tokio::sync::OnceCell;
16
17use super::operations::RecordsOps;
18use crate::core::internal::tx;
19use crate::core::types::{Data, Event, RecordAdded, RecordDeleted, RecordInput};
20use crate::error::Error;
21
22#[derive(Debug, Clone)]
33pub struct AddRecord {
34 pub trail_id: ObjectId,
36 pub owner: Address,
38 pub data: Data,
40 pub metadata: Option<String>,
42 pub tag: Option<String>,
44 pub selected_capability_id: Option<ObjectId>,
46 cached_ptb: OnceCell<ProgrammableTransaction>,
47}
48
49impl AddRecord {
50 pub fn new(
52 trail_id: ObjectId,
53 owner: Address,
54 data: Data,
55 metadata: Option<String>,
56 tag: Option<String>,
57 selected_capability_id: Option<ObjectId>,
58 ) -> Self {
59 Self {
60 trail_id,
61 owner,
62 data,
63 metadata,
64 tag,
65 selected_capability_id,
66 cached_ptb: OnceCell::new(),
67 }
68 }
69
70 async fn make_ptb<C>(&self, client: &C) -> Result<ProgrammableTransaction, Error>
71 where
72 C: CoreClientReadOnly + OptionalSync,
73 {
74 RecordsOps::add_record(
75 client,
76 self.trail_id,
77 self.owner,
78 RecordInput::new(self.data.clone(), self.metadata.clone(), self.tag.clone()),
79 self.selected_capability_id,
80 )
81 .await
82 }
83}
84
85#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))]
86#[cfg_attr(feature = "send-sync", async_trait)]
87impl Transaction for AddRecord {
88 type Error = Error;
89 type Output = RecordAdded;
90
91 async fn build_programmable_transaction<C>(&self, client: &C) -> Result<ProgrammableTransaction, Self::Error>
92 where
93 C: CoreClientReadOnly + OptionalSync,
94 {
95 self.cached_ptb.get_or_try_init(|| self.make_ptb(client)).await.cloned()
96 }
97
98 async fn apply_with_events<C>(
99 mut self,
100 _: &mut IotaTransactionBlockEffects,
101 events: &mut IotaTransactionBlockEvents,
102 _: &C,
103 ) -> Result<Self::Output, Self::Error>
104 where
105 C: CoreClientReadOnly + OptionalSync,
106 {
107 let event = events
108 .data
109 .iter()
110 .find_map(|data| serde_json::from_value::<Event<RecordAdded>>(data.parsed_json.clone()).ok())
111 .ok_or_else(|| Error::UnexpectedApiResponse("RecordAdded event not found".to_string()))?;
112
113 Ok(event.data)
114 }
115
116 async fn apply<C>(self, effects: &mut IotaTransactionBlockEffects, client: &C) -> Result<Self::Output, Self::Error>
117 where
118 C: CoreClientReadOnly + OptionalSync,
119 {
120 tx::apply_with_events(self, effects, client).await
121 }
122}
123
124#[derive(Debug, Clone)]
139pub struct CorrectRecord {
140 pub trail_id: ObjectId,
142 pub owner: Address,
144 pub sequence_number: u64,
146 pub data: Data,
148 pub metadata: Option<String>,
150 pub tag: Option<String>,
152 pub selected_capability_id: Option<ObjectId>,
154 cached_ptb: OnceCell<ProgrammableTransaction>,
155}
156
157impl CorrectRecord {
158 pub fn new(
164 trail_id: ObjectId,
165 owner: Address,
166 sequence_number: u64,
167 data: Data,
168 metadata: Option<String>,
169 tag: Option<String>,
170 selected_capability_id: Option<ObjectId>,
171 ) -> Self {
172 Self {
173 trail_id,
174 owner,
175 sequence_number,
176 data,
177 metadata,
178 tag,
179 selected_capability_id,
180 cached_ptb: OnceCell::new(),
181 }
182 }
183
184 async fn make_ptb<C>(&self, client: &C) -> Result<ProgrammableTransaction, Error>
185 where
186 C: CoreClientReadOnly + OptionalSync,
187 {
188 RecordsOps::correct_record(
189 client,
190 self.trail_id,
191 self.owner,
192 self.sequence_number,
193 RecordInput::new(self.data.clone(), self.metadata.clone(), self.tag.clone()),
194 self.selected_capability_id,
195 )
196 .await
197 }
198}
199
200#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))]
201#[cfg_attr(feature = "send-sync", async_trait)]
202impl Transaction for CorrectRecord {
203 type Error = Error;
204 type Output = RecordAdded;
205
206 async fn build_programmable_transaction<C>(&self, client: &C) -> Result<ProgrammableTransaction, Self::Error>
207 where
208 C: CoreClientReadOnly + OptionalSync,
209 {
210 self.cached_ptb.get_or_try_init(|| self.make_ptb(client)).await.cloned()
211 }
212
213 async fn apply_with_events<C>(
214 mut self,
215 _: &mut IotaTransactionBlockEffects,
216 events: &mut IotaTransactionBlockEvents,
217 _: &C,
218 ) -> Result<Self::Output, Self::Error>
219 where
220 C: CoreClientReadOnly + OptionalSync,
221 {
222 let event = events
223 .data
224 .iter()
225 .find_map(|data| serde_json::from_value::<Event<RecordAdded>>(data.parsed_json.clone()).ok())
226 .ok_or_else(|| Error::UnexpectedApiResponse("RecordAdded event not found".to_string()))?;
227
228 Ok(event.data)
229 }
230
231 async fn apply<C>(self, effects: &mut IotaTransactionBlockEffects, client: &C) -> Result<Self::Output, Self::Error>
232 where
233 C: CoreClientReadOnly + OptionalSync,
234 {
235 tx::apply_with_events(self, effects, client).await
236 }
237}
238
239#[derive(Debug, Clone)]
250pub struct DeleteRecord {
251 pub trail_id: ObjectId,
253 pub owner: Address,
255 pub sequence_number: u64,
257 pub selected_capability_id: Option<ObjectId>,
259 cached_ptb: OnceCell<ProgrammableTransaction>,
260}
261
262impl DeleteRecord {
263 pub fn new(
265 trail_id: ObjectId,
266 owner: Address,
267 sequence_number: u64,
268 selected_capability_id: Option<ObjectId>,
269 ) -> Self {
270 Self {
271 trail_id,
272 owner,
273 sequence_number,
274 selected_capability_id,
275 cached_ptb: OnceCell::new(),
276 }
277 }
278
279 async fn make_ptb<C>(&self, client: &C) -> Result<ProgrammableTransaction, Error>
280 where
281 C: CoreClientReadOnly + OptionalSync,
282 {
283 RecordsOps::delete_record(
284 client,
285 self.trail_id,
286 self.owner,
287 self.sequence_number,
288 self.selected_capability_id,
289 )
290 .await
291 }
292}
293
294#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))]
295#[cfg_attr(feature = "send-sync", async_trait)]
296impl Transaction for DeleteRecord {
297 type Error = Error;
298 type Output = RecordDeleted;
299
300 async fn build_programmable_transaction<C>(&self, client: &C) -> Result<ProgrammableTransaction, Self::Error>
301 where
302 C: CoreClientReadOnly + OptionalSync,
303 {
304 self.cached_ptb.get_or_try_init(|| self.make_ptb(client)).await.cloned()
305 }
306
307 async fn apply_with_events<C>(
308 mut self,
309 _: &mut IotaTransactionBlockEffects,
310 events: &mut IotaTransactionBlockEvents,
311 _: &C,
312 ) -> Result<Self::Output, Self::Error>
313 where
314 C: CoreClientReadOnly + OptionalSync,
315 {
316 let event = events
317 .data
318 .iter()
319 .find_map(|data| serde_json::from_value::<Event<RecordDeleted>>(data.parsed_json.clone()).ok())
320 .ok_or_else(|| Error::UnexpectedApiResponse("RecordDeleted event not found".to_string()))?;
321
322 Ok(event.data)
323 }
324
325 async fn apply<C>(self, effects: &mut IotaTransactionBlockEffects, client: &C) -> Result<Self::Output, Self::Error>
326 where
327 C: CoreClientReadOnly + OptionalSync,
328 {
329 tx::apply_with_events(self, effects, client).await
330 }
331}
332
333#[derive(Debug, Clone)]
353pub struct DeleteRecordsBatch {
354 pub trail_id: ObjectId,
356 pub owner: Address,
358 pub limit: u64,
360 pub selected_capability_id: Option<ObjectId>,
362 cached_ptb: OnceCell<ProgrammableTransaction>,
363}
364
365impl DeleteRecordsBatch {
366 pub fn new(trail_id: ObjectId, owner: Address, limit: u64, selected_capability_id: Option<ObjectId>) -> Self {
368 Self {
369 trail_id,
370 owner,
371 limit,
372 selected_capability_id,
373 cached_ptb: OnceCell::new(),
374 }
375 }
376
377 async fn make_ptb<C>(&self, client: &C) -> Result<ProgrammableTransaction, Error>
378 where
379 C: CoreClientReadOnly + OptionalSync,
380 {
381 RecordsOps::delete_records_batch(
382 client,
383 self.trail_id,
384 self.owner,
385 self.limit,
386 self.selected_capability_id,
387 )
388 .await
389 }
390}
391
392#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))]
393#[cfg_attr(feature = "send-sync", async_trait)]
394impl Transaction for DeleteRecordsBatch {
395 type Error = Error;
396 type Output = Vec<u64>;
397
398 async fn build_programmable_transaction<C>(&self, client: &C) -> Result<ProgrammableTransaction, Self::Error>
399 where
400 C: CoreClientReadOnly + OptionalSync,
401 {
402 self.cached_ptb.get_or_try_init(|| self.make_ptb(client)).await.cloned()
403 }
404
405 async fn apply_with_events<C>(
406 self,
407 _: &mut IotaTransactionBlockEffects,
408 events: &mut IotaTransactionBlockEvents,
409 _: &C,
410 ) -> Result<Self::Output, Self::Error>
411 where
412 C: CoreClientReadOnly + OptionalSync,
413 {
414 let deleted = events
415 .data
416 .iter()
417 .filter_map(|data| serde_json::from_value::<Event<RecordDeleted>>(data.parsed_json.clone()).ok())
418 .map(|event| event.data.sequence_number)
419 .collect();
420
421 Ok(deleted)
422 }
423
424 async fn apply<C>(self, effects: &mut IotaTransactionBlockEffects, client: &C) -> Result<Self::Output, Self::Error>
425 where
426 C: CoreClientReadOnly + OptionalSync,
427 {
428 tx::apply_with_events(self, effects, client).await
429 }
430}