Skip to main content

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<T> {
86    #[serde(rename = "ERROR")]
87    Error { reason: String },
88    #[serde(rename = "OK")]
89    Ok(T),
90}
91
92#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
93pub struct EventResponse {
94    pub event: Option<String>,
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn response_from_str() {
103        let tests = vec![
104            (
105                r#"{"status":"ERROR","reason":"error detail..."}"#,
106                Response::Error {
107                    reason: "error detail...".to_string(),
108                },
109            ),
110            (
111                r#"{"status":"OK","event":"LOGGEDIN"}"#,
112                Response::<EventResponse>::Ok(EventResponse {
113                    event: Some("LOGGEDIN".to_string()),
114                }),
115            ),
116        ];
117
118        for test in tests {
119            let resp: Response<_> = serde_json::from_str(test.0).unwrap();
120            assert_eq!(resp, test.1);
121        }
122    }
123    #[test]
124    fn response_to_str() {
125        let tests = vec![
126            (
127                r#"{"status":"ERROR","reason":"error detail..."}"#,
128                Response::Error {
129                    reason: "error detail...".to_string(),
130                },
131            ),
132            (
133                r#"{"status":"OK","event":"LOGGEDIN"}"#,
134                Response::<EventResponse>::Ok(EventResponse {
135                    event: Some("LOGGEDIN".to_string()),
136                }),
137            ),
138        ];
139
140        for test in tests {
141            let json = serde_json::to_string(&test.1).unwrap();
142            assert_eq!(json, test.0);
143        }
144    }
145}