1use serde::{Deserialize, Deserializer, Serialize};
2use serde_json;
3
4use crate::Error;
5
6use base64::{engine::general_purpose, Engine as _};
7
8pub mod builder;
9
10pub fn from_b64(b64: &str) -> Result<Boostagram, Error> {
11 let json_raw = general_purpose::STANDARD.decode(b64)?;
12 serde_json::from_slice(&json_raw).map_err(Error::from)
13}
14
15pub fn from_json(json: &str) -> Result<Boostagram, Error> {
16 serde_json::from_str(json).map_err(Error::from)
17}
18
19#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
20pub enum Action {
21 #[serde(rename = "stream")]
22 Stream,
23 #[serde(rename = "boost")]
24 Boost,
25 #[serde(rename = "lsat")]
26 Lsat,
27 #[serde(rename = "auto")]
28 Auto,
29 #[serde(other)]
30 Unknown,
31}
32
33#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
34pub struct Boostagram {
35 pub podcast: Option<String>,
36
37 #[serde(rename = "feedID")]
38 pub feed_id: Option<usize>,
39
40 pub url: Option<String>,
41
42 pub guid: Option<String>,
43
44 pub episode: Option<String>,
45
46 #[serde(rename = "itemID")]
47 #[serde(deserialize_with = "deserialize_item_id")]
48 #[serde(default)]
49 pub item_id: Option<usize>,
50
51 pub episode_guid: Option<String>,
52
53 pub time: Option<String>,
54
55 pub ts: Option<usize>,
56
57 pub action: Option<Action>,
58
59 pub app_name: Option<String>,
60
61 pub app_version: Option<String>,
62
63 pub boost_link: Option<String>,
64
65 pub message: Option<String>,
66
67 pub name: Option<String>,
68
69 pub sender_name: Option<String>,
70
71 pub sender_id: Option<String>,
72
73 pub signature: Option<String>,
74
75 pub speed: Option<String>,
76
77 pub uuid: Option<String>,
78
79 pub value_msat_total: Option<u64>,
81
82 pub reply_address: Option<String>,
83
84 pub reply_custom_key: Option<usize>,
86
87 pub reply_custom_value: Option<String>,
88}
89
90fn deserialize_item_id<'de, D>(d: D) -> Result<Option<usize>, D::Error>
91where
92 D: Deserializer<'de>,
93{
94 Deserialize::deserialize(d).or(Ok(Option::None))
95}
96
97impl Boostagram {
98 pub fn to_b64(&self) -> Result<String, Error> {
99 let json = serde_json::to_vec(self)?;
100 Ok(general_purpose::STANDARD.encode(json))
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_from_fountain_invoice() {
110 let boost_raw = "eyJhcHBfbmFtZSI6IkZvdW50YWluIiwidmFsdWVfbXNhdF90b3RhbCI6MTAwMDAwLCJuYW1lIjoiQWxieSBUZXN0IFVzZXIgUFVUIiwicG9kY2FzdCI6IlRlc3QgUG9kY2FzdCBBbmNob3IiLCJmZWVkSUQiOjYwMTU2NzEsImFjdGlvbiI6ImJvb3N0Iiwic2VuZGVyX2lkIjoiblNpcTdpZDc4SkFkSDl1WTFwSXkiLCJzZW5kZXJfbmFtZSI6IkBhbHdpbl9jb25zaGF4IiwibWVzc2FnZSI6InRlc3QiLCJpdGVtSUQiOiIxNDkzNDE1NDMwOSIsImJvb3N0X2xpbmsiOiJodHRwczovL2ZvdW50YWluLmZtL2VwaXNvZGUvMTQ5MzQxNTQzMDkiLCJlcGlzb2RlIjoidGhpcyBpcyBhIHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgdmVyeSB2ZXJ5IHZlcnkgbG9uZyBlcGlzb2RlIG5hbWUhISEiLCJ0cyI6MzI5LCJ0aW1lIjoiMDA6MDU6MjkifQ==";
111
112 let result = from_b64(boost_raw);
113
114 dbg!(&result);
115
116 assert!(result.is_ok());
117
118 let boostagram = result.unwrap();
119
120 assert_eq!(boostagram.item_id, None);
121 }
122}