1use alloc::rc::Rc;
2use core::cell::RefCell;
3
4use umsh_mac::{LocalIdentityId, SendReceipt};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub struct SendToken {
14 pub identity_id: LocalIdentityId,
15 pub receipt: SendReceipt,
16}
17
18impl SendToken {
19 pub fn new(identity_id: LocalIdentityId, receipt: SendReceipt) -> Self {
21 Self {
22 identity_id,
23 receipt,
24 }
25 }
26}
27
28#[derive(Clone, Debug, Default)]
30pub(crate) struct TicketState {
31 pub transmitted: bool,
33 pub repeated: bool,
35 pub acked: bool,
37 pub failed: bool,
39 pub finished: bool,
41 pub non_ack: bool,
44}
45
46pub struct SendProgressTicket {
56 token: Option<SendToken>,
57 state: Rc<RefCell<TicketState>>,
58}
59
60impl SendProgressTicket {
61 pub(crate) fn new(token: SendToken, state: Rc<RefCell<TicketState>>) -> Self {
63 Self {
64 token: Some(token),
65 state,
66 }
67 }
68
69 pub(crate) fn fire_and_forget() -> Self {
75 let state = Rc::new(RefCell::new(TicketState {
76 transmitted: true,
77 finished: true,
78 non_ack: true,
79 ..TicketState::default()
80 }));
81 Self { token: None, state }
82 }
83
84 pub fn token(&self) -> Option<SendToken> {
86 self.token
87 }
88
89 pub fn receipt(&self) -> Option<SendReceipt> {
91 self.token.map(|t| t.receipt)
92 }
93
94 pub fn was_transmitted(&self) -> bool {
100 self.state.borrow().transmitted
101 }
102
103 pub fn was_repeated(&self) -> bool {
105 self.state.borrow().repeated
106 }
107
108 pub fn was_acked(&self) -> bool {
110 self.state.borrow().acked
111 }
112
113 pub fn has_failed(&self) -> bool {
115 self.state.borrow().failed
116 }
117
118 pub fn is_finished(&self) -> bool {
126 self.state.borrow().finished
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use umsh_mac::{LocalIdentityId, SendReceipt};
134
135 fn make_token() -> SendToken {
136 SendToken::new(LocalIdentityId(0), SendReceipt(42))
137 }
138
139 fn make_ticket() -> (SendProgressTicket, Rc<RefCell<TicketState>>) {
140 let state = Rc::new(RefCell::new(TicketState::default()));
141 let ticket = SendProgressTicket::new(make_token(), state.clone());
142 (ticket, state)
143 }
144
145 #[test]
146 fn new_ticket_all_false() {
147 let (ticket, _state) = make_ticket();
148 assert!(!ticket.was_transmitted());
149 assert!(!ticket.was_repeated());
150 assert!(!ticket.was_acked());
151 assert!(!ticket.has_failed());
152 assert!(!ticket.is_finished());
153 }
154
155 #[test]
156 fn new_ticket_token_and_receipt() {
157 let (ticket, _state) = make_ticket();
158 let token = ticket.token().unwrap();
159 assert_eq!(token.identity_id, LocalIdentityId(0));
160 assert_eq!(token.receipt, SendReceipt(42));
161 assert_eq!(ticket.receipt(), Some(SendReceipt(42)));
162 }
163
164 #[test]
165 fn fire_and_forget_initially_transmitted_and_finished() {
166 let ticket = SendProgressTicket::fire_and_forget();
167 assert!(ticket.was_transmitted());
168 assert!(ticket.is_finished());
169 assert!(!ticket.was_acked());
170 assert!(!ticket.has_failed());
171 assert!(ticket.token().is_none());
172 assert!(ticket.receipt().is_none());
173 }
174
175 #[test]
176 fn state_mutations_visible_through_ticket() {
177 let (ticket, state) = make_ticket();
178
179 state.borrow_mut().transmitted = true;
180 assert!(ticket.was_transmitted());
181
182 state.borrow_mut().repeated = true;
183 assert!(ticket.was_repeated());
184
185 state.borrow_mut().acked = true;
186 assert!(ticket.was_acked());
187
188 state.borrow_mut().failed = true;
189 assert!(ticket.has_failed());
190
191 state.borrow_mut().finished = true;
192 assert!(ticket.is_finished());
193 }
194
195 #[test]
196 fn cloned_state_reflects_same_updates() {
197 let (ticket, state) = make_ticket();
198 let ticket2 = SendProgressTicket::new(make_token(), state.clone());
199 state.borrow_mut().acked = true;
200 assert!(ticket.was_acked());
201 assert!(ticket2.was_acked());
202 }
203}