lnurl/
api.rs

1use crate::channel::ChannelResponse;
2use crate::pay::PayResponse;
3use crate::withdraw::WithdrawalResponse;
4use crate::Error as LnUrlError;
5use serde::de::Error;
6use serde::{Deserialize, Serialize};
7use std::fmt::Display;
8use std::str::FromStr;
9
10pub fn decode_ln_url_response(string: &str) -> Result<LnUrlResponse, LnUrlError> {
11    let json: serde_json::Value = serde_json::from_str(string)?;
12    decode_ln_url_response_from_json(json)
13}
14
15pub fn decode_ln_url_response_from_json(
16    json: serde_json::Value,
17) -> Result<LnUrlResponse, LnUrlError> {
18    let obj = json.as_object().ok_or(LnUrlError::InvalidResponse)?;
19    let tag_str = obj
20        .get("tag")
21        .and_then(|v| v.as_str())
22        .ok_or(LnUrlError::InvalidResponse)?;
23
24    let tag = Tag::from_str(tag_str)?;
25    match tag {
26        Tag::PayRequest => {
27            let pay_response: PayResponse = serde_json::from_value(json)?;
28            Ok(LnUrlResponse::LnUrlPayResponse(pay_response))
29        }
30        Tag::WithdrawRequest => {
31            let resp: WithdrawalResponse = serde_json::from_value(json)?;
32            Ok(LnUrlResponse::LnUrlWithdrawResponse(resp))
33        }
34        Tag::ChannelRequest => {
35            let resp: ChannelResponse = serde_json::from_value(json)?;
36            Ok(LnUrlResponse::LnUrlChannelResponse(resp))
37        }
38    }
39}
40
41#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
42pub enum LnUrlResponse {
43    LnUrlPayResponse(PayResponse),
44    LnUrlWithdrawResponse(WithdrawalResponse),
45    LnUrlChannelResponse(ChannelResponse),
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub enum Tag {
50    #[serde(rename = "payRequest")]
51    PayRequest,
52    #[serde(rename = "withdrawRequest")]
53    WithdrawRequest,
54    #[serde(rename = "channelRequest")]
55    ChannelRequest,
56}
57
58impl Display for Tag {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        match self {
61            Tag::PayRequest => write!(f, "payRequest"),
62            Tag::WithdrawRequest => write!(f, "withdrawRequest"),
63            Tag::ChannelRequest => write!(f, "channelRequest"),
64        }
65    }
66}
67
68impl FromStr for Tag {
69    type Err = serde_json::Error;
70
71    fn from_str(s: &str) -> Result<Self, Self::Err> {
72        match s {
73            "payRequest" => Ok(Tag::PayRequest),
74            "withdrawRequest" => Ok(Tag::WithdrawRequest),
75            "channelRequest" => Ok(Tag::ChannelRequest),
76            _ => Err(serde_json::Error::custom("Unknown tag")),
77        }
78    }
79}
80
81/// Response is the response format returned by Service.
82/// Example: `{\"status\":\"ERROR\",\"reason\":\"error detail...\"}"`
83#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
84#[serde(tag = "status")]
85pub enum Response {
86    #[serde(rename = "ERROR")]
87    Error { reason: String },
88    #[serde(rename = "OK")]
89    Ok { event: Option<String> },
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn response_from_str() {
98        let tests = vec![
99            (
100                r#"{"status":"ERROR","reason":"error detail..."}"#,
101                Response::Error {
102                    reason: "error detail...".to_string(),
103                },
104            ),
105            (
106                r#"{"status":"OK","event":"LOGGEDIN"}"#,
107                Response::Ok {
108                    event: Some("LOGGEDIN".to_string()),
109                },
110            ),
111        ];
112
113        for test in tests {
114            let resp: Response = serde_json::from_str(test.0).unwrap();
115            assert_eq!(resp, test.1);
116        }
117    }
118    #[test]
119    fn response_to_str() {
120        let tests = vec![
121            (
122                r#"{"status":"ERROR","reason":"error detail..."}"#,
123                Response::Error {
124                    reason: "error detail...".to_string(),
125                },
126            ),
127            (
128                r#"{"status":"OK","event":"LOGGEDIN"}"#,
129                Response::Ok {
130                    event: Some("LOGGEDIN".to_string()),
131                },
132            ),
133        ];
134
135        for test in tests {
136            let json = serde_json::to_string(&test.1).unwrap();
137            assert_eq!(json, test.0);
138        }
139    }
140}