1use std::io::{Read, Write};
3
4use crate::error::{Error, Result};
5use crate::xdr;
6
7pub const MAX_MEMO_TEXT_LEN: usize = 28;
9
10pub const MAX_HASH_LEN: usize = 32;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum Memo {
16 None,
18 Text(String),
20 Id(u64),
22 Hash([u8; 32]),
24 Return([u8; 32]),
26}
27
28impl Memo {
29 pub fn new_none() -> Memo {
31 Memo::None
32 }
33
34 pub fn new_id(id: u64) -> Memo {
36 Memo::Id(id)
37 }
38
39 pub fn new_text<S: Into<String>>(text: S) -> Result<Memo> {
41 let text = text.into();
42 if text.len() > MAX_MEMO_TEXT_LEN {
43 Err(Error::InvalidMemoText)
44 } else {
45 Ok(Memo::Text(text))
46 }
47 }
48
49 pub fn new_hash(hash: &[u8]) -> Result<Memo> {
51 if hash.len() > MAX_HASH_LEN {
52 Err(Error::InvalidMemoHash)
53 } else {
54 let mut memo_hash: [u8; 32] = Default::default();
55 memo_hash[..hash.len()].copy_from_slice(hash);
56 Ok(Memo::Hash(memo_hash))
57 }
58 }
59
60 pub fn new_return(ret: &[u8]) -> Result<Memo> {
62 if ret.len() > MAX_HASH_LEN {
63 Err(Error::InvalidMemoReturn)
64 } else {
65 let mut memo_ret: [u8; 32] = Default::default();
66 memo_ret[..ret.len()].copy_from_slice(ret);
67 Ok(Memo::Return(memo_ret))
68 }
69 }
70
71 pub fn is_none(&self) -> bool {
73 matches!(self, Memo::None)
74 }
75
76 pub fn as_id(&self) -> Option<&u64> {
78 match *self {
79 Memo::Id(ref id) => Some(id),
80 _ => None,
81 }
82 }
83
84 pub fn as_id_mut(&mut self) -> Option<&mut u64> {
86 match *self {
87 Memo::Id(ref mut id) => Some(id),
88 _ => None,
89 }
90 }
91
92 pub fn is_id(&self) -> bool {
94 self.as_id().is_some()
95 }
96
97 pub fn as_text(&self) -> Option<&str> {
99 match *self {
100 Memo::Text(ref text) => Some(text),
101 _ => None,
102 }
103 }
104
105 pub fn as_text_mut(&mut self) -> Option<&mut str> {
107 match *self {
108 Memo::Text(ref mut text) => Some(text),
109 _ => None,
110 }
111 }
112
113 pub fn is_text(&self) -> bool {
115 self.as_text().is_some()
116 }
117
118 pub fn as_hash(&self) -> Option<&[u8; 32]> {
120 match *self {
121 Memo::Hash(ref hash) => Some(hash),
122 _ => None,
123 }
124 }
125
126 pub fn as_hash_mut(&mut self) -> Option<&mut [u8; 32]> {
128 match *self {
129 Memo::Hash(ref mut hash) => Some(hash),
130 _ => None,
131 }
132 }
133
134 pub fn is_hash(&self) -> bool {
136 self.as_hash().is_some()
137 }
138
139 pub fn as_return(&self) -> Option<&[u8; 32]> {
141 match *self {
142 Memo::Return(ref hash) => Some(hash),
143 _ => None,
144 }
145 }
146
147 pub fn as_return_mut(&mut self) -> Option<&mut [u8; 32]> {
149 match *self {
150 Memo::Return(ref mut hash) => Some(hash),
151 _ => None,
152 }
153 }
154
155 pub fn is_return(&self) -> bool {
157 self.as_return().is_some()
158 }
159
160 pub fn to_xdr(&self) -> Result<xdr::Memo> {
162 match self {
163 Memo::None => Ok(xdr::Memo::None),
164 Memo::Text(text) => Ok(xdr::Memo::Text(
165 text.try_into().map_err(|_| Error::InvalidMemoText)?,
166 )),
167 Memo::Id(id) => Ok(xdr::Memo::Id(*id)),
168 Memo::Hash(hash) => Ok(xdr::Memo::Hash(hash.clone().into())),
169 Memo::Return(ret) => Ok(xdr::Memo::Return(ret.clone().into())),
170 }
171 }
172
173 pub fn from_xdr(x: &xdr::Memo) -> Result<Memo> {
175 match x {
176 xdr::Memo::None => Ok(Memo::new_none()),
177 xdr::Memo::Text(text) => {
178 let string: String = text.try_into().map_err(|_| Error::InvalidMemoText)?;
179 Memo::new_text(string)
180 }
181 xdr::Memo::Id(id) => Ok(Memo::new_id(*id)),
182 xdr::Memo::Hash(hash) => Memo::new_hash(&hash.0),
183 xdr::Memo::Return(ret) => Memo::new_return(&ret.0),
184 }
185 }
186}
187
188impl Default for Memo {
189 fn default() -> Memo {
190 Memo::new_none()
191 }
192}
193
194impl xdr::WriteXdr for Memo {
195 fn write_xdr<W: Write>(&self, w: &mut xdr::Limited<W>) -> xdr::Result<()> {
196 let xdr_memo = self.to_xdr().map_err(|_| xdr::Error::Invalid)?;
197 xdr_memo.write_xdr(w)
198 }
199}
200
201impl xdr::ReadXdr for Memo {
202 fn read_xdr<R: Read>(r: &mut xdr::Limited<R>) -> xdr::Result<Self> {
203 let xdr_result = xdr::Memo::read_xdr(r)?;
204 Self::from_xdr(&xdr_result).map_err(|_| xdr::Error::Invalid)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::Memo;
211 use crate::xdr::{XDRDeserialize, XDRSerialize};
212
213 #[test]
214 fn test_defaults_to_none() {
215 let memo: Memo = Default::default();
216 assert!(memo.is_none());
217 }
218
219 #[test]
220 fn test_memo_none() {
221 let memo = Memo::new_none();
222 assert!(memo.is_none());
223 assert!(!memo.is_id());
224 assert!(!memo.is_text());
225 assert!(!memo.is_hash());
226 assert!(!memo.is_return());
227
228 assert_eq!(None, memo.as_id());
229 assert_eq!(None, memo.as_text());
230 assert_eq!(None, memo.as_hash());
231 assert_eq!(None, memo.as_return());
232 }
233
234 #[test]
235 fn test_memo_id() {
236 let mut memo = Memo::new_id(1234);
237 assert!(!memo.is_none());
238 assert!(memo.is_id());
239 assert!(!memo.is_text());
240 assert!(!memo.is_hash());
241 assert!(!memo.is_return());
242
243 assert_eq!(Some(&1234), memo.as_id());
244 assert_eq!(None, memo.as_text());
245 assert_eq!(None, memo.as_hash());
246 assert_eq!(None, memo.as_return());
247
248 *memo.as_id_mut().unwrap() = 456;
249 assert_eq!(Some(&456), memo.as_id());
250 }
251
252 #[test]
253 fn test_memo_text() {
254 let memo = Memo::new_text("Short text memo").unwrap();
255 assert!(!memo.is_none());
256 assert!(!memo.is_id());
257 assert!(memo.is_text());
258 assert!(!memo.is_hash());
259 assert!(!memo.is_return());
260
261 assert_eq!(None, memo.as_id());
262 assert_eq!("Short text memo", memo.as_text().unwrap());
263 assert_eq!(None, memo.as_hash());
264 assert_eq!(None, memo.as_return());
265 }
266
267 #[test]
268 fn test_memo_hash() {
269 let mut memo = Memo::new_hash(&[1, 2, 3, 4, 5]).unwrap();
270 assert!(!memo.is_none());
271 assert!(!memo.is_id());
272 assert!(!memo.is_text());
273 assert!(memo.is_hash());
274 assert!(!memo.is_return());
275
276 assert_eq!(None, memo.as_id());
277 assert_eq!(None, memo.as_text());
278 assert_eq!(
279 vec![
280 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
281 0, 0, 0, 0
282 ],
283 memo.as_hash().unwrap()
284 );
285 assert_eq!(None, memo.as_return());
286
287 memo.as_hash_mut().unwrap()[super::MAX_HASH_LEN - 1] = 9;
288
289 assert_eq!(
290 vec![
291 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
292 0, 0, 0, 9
293 ],
294 memo.as_hash().unwrap()
295 );
296 }
297
298 #[test]
299 fn test_memo_return() {
300 let mut memo = Memo::new_return(&[1, 2, 3, 4, 5]).unwrap();
301 assert!(!memo.is_none());
302 assert!(!memo.is_id());
303 assert!(!memo.is_text());
304 assert!(!memo.is_hash());
305 assert!(memo.is_return());
306
307 assert_eq!(None, memo.as_id());
308 assert_eq!(None, memo.as_text());
309 assert_eq!(None, memo.as_hash());
310 assert_eq!(
311 vec![
312 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
313 0, 0, 0, 0
314 ],
315 memo.as_return().unwrap()
316 );
317
318 memo.as_return_mut().unwrap()[super::MAX_HASH_LEN - 1] = 9;
319 assert_eq!(
320 vec![
321 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
322 0, 0, 0, 9
323 ],
324 memo.as_return().unwrap()
325 );
326 }
327
328 #[test]
329 fn test_memo_text_too_long() {
330 let result =
331 Memo::new_text("This is a very long text that will not fit in the memo 100% sure.");
332 assert!(result.is_err());
333 }
334
335 #[test]
336 fn test_memo_hash_too_long() {
337 let mut hash = Vec::new();
338 hash.resize(33, b'1');
339 let result = Memo::new_hash(&hash);
340 assert!(result.is_err());
341 }
342
343 #[test]
344 fn test_memo_return_too_long() {
345 let mut hash = Vec::new();
346 hash.resize(33, b'1');
347 let result = Memo::new_return(&hash);
348 assert!(result.is_err());
349 }
350
351 #[test]
352 fn test_memo_none_xdr_ser_de() {
353 let original = Memo::new_none();
354 let xdr = original.xdr_base64().unwrap();
355 assert_eq!("AAAAAA==", xdr);
356 let back = Memo::from_xdr_base64(&xdr).unwrap();
357 assert_eq!(original, back);
358 }
359
360 #[test]
361 fn test_memo_id_xdr_ser_de() {
362 let original = Memo::new_id(u64::MAX);
363 let xdr = original.xdr_base64().unwrap();
364 assert_eq!("AAAAAv//////////", xdr);
365 let back = Memo::from_xdr_base64(&xdr).unwrap();
366 assert_eq!(original, back);
367 }
368
369 #[test]
370 fn test_memo_hash_xdr_ser_de() {
371 let mut hash = Vec::new();
372 for i in 0..32 {
373 hash.push(i as u8);
374 }
375 let original = Memo::new_hash(&hash).unwrap();
376 let xdr = original.xdr_base64().unwrap();
377 assert_eq!("AAAAAwABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4f", xdr);
378 let back = Memo::from_xdr_base64(&xdr).unwrap();
379 assert_eq!(original, back);
380 }
381
382 #[test]
383 fn test_return_hash_xdr_ser_de() {
384 let mut hash = Vec::new();
385 for i in 0..32 {
386 hash.push(i as u8);
387 }
388 let original = Memo::new_return(&hash).unwrap();
389 let xdr = original.xdr_base64().unwrap();
390 assert_eq!("AAAABAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4f", xdr);
391 let back = Memo::from_xdr_base64(&xdr).unwrap();
392 assert_eq!(original, back);
393 }
394}