1#![forbid(unsafe_code)]
2use core::fmt;
5
6use hop_actions::Action;
7use hop_channels::{Channel, ChannelError};
8
9#[cfg(feature = "net")]
10pub mod net;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct BundleReceipt {
15 pub start_height: u64,
17 pub end_height: u64,
19 pub committed: usize,
21}
22
23#[derive(Debug)]
25pub struct BundleError {
26 pub action_index: usize,
28 pub committed: usize,
30 pub source: ChannelError,
32}
33
34impl fmt::Display for BundleError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(
37 f,
38 "bundle action {} failed after {} commits: {}",
39 self.action_index, self.committed, self.source
40 )
41 }
42}
43
44impl std::error::Error for BundleError {
45 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
46 Some(&self.source)
47 }
48}
49
50pub fn apply_bundle(
56 channel: &mut Channel,
57 actions: &[Action],
58) -> Result<BundleReceipt, BundleError> {
59 for (index, action) in actions.iter().enumerate() {
60 if let Err(source) = action
61 .validate_for_dimension(channel.dimension())
62 .map_err(ChannelError::from)
63 {
64 return Err(BundleError {
65 action_index: index,
66 committed: 0,
67 source,
68 });
69 }
70 }
71
72 let start_height = channel.height();
73 for (index, action) in actions.iter().enumerate() {
74 if let Err(source) = channel.apply(action) {
75 return Err(BundleError {
76 action_index: index,
77 committed: index,
78 source,
79 });
80 }
81 }
82 Ok(BundleReceipt {
83 start_height,
84 end_height: channel.height(),
85 committed: actions.len(),
86 })
87}