1use axum::{
2 extract::{Path, RawQuery},
3 http::StatusCode,
4 routing::get,
5 Router,
6};
7use std::future::Future;
8
9pub struct Server<AR, CE, CC, PE, PC, WE, WC> {
10 auth_request: AR,
11 channel_entrypoint: CE,
12 channel_callback: CC,
13 pay_entrypoint: PE,
14 pay_callback: PC,
15 withdraw_entrypoint: WE,
16 withdraw_callback: WC,
17}
18
19impl Default
20 for Server<
21 unimplemented::Handler<crate::auth::server::Callback, crate::CallbackResponse>,
23 unimplemented::Handler<(), crate::channel::server::Entrypoint>,
25 unimplemented::Handler<crate::channel::server::Callback, crate::CallbackResponse>,
26 unimplemented::Handler<Option<String>, crate::pay::server::Entrypoint>,
28 unimplemented::Handler<crate::pay::server::Callback, crate::pay::server::CallbackResponse>,
29 unimplemented::Handler<(), crate::withdraw::server::Entrypoint>,
31 unimplemented::Handler<crate::withdraw::server::Callback, crate::CallbackResponse>,
32 >
33{
34 fn default() -> Self {
35 Server {
36 auth_request: unimplemented::handler,
37
38 channel_entrypoint: unimplemented::handler,
39 channel_callback: unimplemented::handler,
40
41 pay_entrypoint: unimplemented::handler,
42 pay_callback: unimplemented::handler,
43
44 withdraw_entrypoint: unimplemented::handler,
45 withdraw_callback: unimplemented::handler,
46 }
47 }
48}
49
50impl<AR, CE, CC, PE, PC, WE, WC> Server<AR, CE, CC, PE, PC, WE, WC> {
51 pub fn auth<AR2>(self, auth_request: AR2) -> Server<AR2, CE, CC, PE, PC, WE, WC> {
52 Server {
53 auth_request,
54 channel_entrypoint: self.channel_entrypoint,
55 channel_callback: self.channel_callback,
56 pay_entrypoint: self.pay_entrypoint,
57 pay_callback: self.pay_callback,
58 withdraw_entrypoint: self.withdraw_entrypoint,
59 withdraw_callback: self.withdraw_callback,
60 }
61 }
62
63 pub fn channel_request<CE2, CC2>(
64 self,
65 channel_entrypoint: CE2,
66 channel_callback: CC2,
67 ) -> Server<AR, CE2, CC2, PE, PC, WE, WC> {
68 Server {
69 auth_request: self.auth_request,
70 channel_entrypoint,
71 channel_callback,
72 pay_entrypoint: self.pay_entrypoint,
73 pay_callback: self.pay_callback,
74 withdraw_entrypoint: self.withdraw_entrypoint,
75 withdraw_callback: self.withdraw_callback,
76 }
77 }
78
79 pub fn pay_request<PE2, PC2>(
80 self,
81 pay_entrypoint: PE2,
82 pay_callback: PC2,
83 ) -> Server<AR, CE, CC, PE2, PC2, WE, WC> {
84 Server {
85 auth_request: self.auth_request,
86 channel_entrypoint: self.channel_entrypoint,
87 channel_callback: self.channel_callback,
88 pay_entrypoint,
89 pay_callback,
90 withdraw_entrypoint: self.withdraw_entrypoint,
91 withdraw_callback: self.withdraw_callback,
92 }
93 }
94
95 pub fn withdraw_request<WE2, WC2>(
96 self,
97 withdraw_entrypoint: WE2,
98 withdraw_callback: WC2,
99 ) -> Server<AR, CE, CC, PE, PC, WE2, WC2> {
100 Server {
101 auth_request: self.auth_request,
102 channel_entrypoint: self.channel_entrypoint,
103 channel_callback: self.channel_callback,
104 pay_entrypoint: self.pay_entrypoint,
105 pay_callback: self.pay_callback,
106 withdraw_entrypoint,
107 withdraw_callback,
108 }
109 }
110}
111
112impl<AR, ARFut, CE, CQFut, CC, CCFut, PE, PEFut, PC, PCFut, WE, WEFut, WC, WCFut>
113 Server<AR, CE, CC, PE, PC, WE, WC>
114where
115 AR: 'static + Send + Clone + Fn(crate::auth::server::Callback) -> ARFut,
116 ARFut: Send + Future<Output = Result<crate::CallbackResponse, StatusCode>>,
117
118 CE: 'static + Send + Clone + Fn(()) -> CQFut,
119 CQFut: Send + Future<Output = Result<crate::channel::server::Entrypoint, StatusCode>>,
120
121 CC: 'static + Send + Clone + Fn(crate::channel::server::Callback) -> CCFut,
122 CCFut: Send + Future<Output = Result<crate::CallbackResponse, StatusCode>>,
123
124 PE: 'static + Send + Clone + Fn(Option<String>) -> PEFut,
125 PEFut: Send + Future<Output = Result<crate::pay::server::Entrypoint, StatusCode>>,
126
127 PC: 'static + Send + Clone + Fn(crate::pay::server::Callback) -> PCFut,
128 PCFut: Send + Future<Output = Result<crate::pay::server::CallbackResponse, StatusCode>>,
129
130 WE: 'static + Send + Clone + Fn(()) -> WEFut,
131 WEFut: Send + Future<Output = Result<crate::withdraw::server::Entrypoint, StatusCode>>,
132
133 WC: 'static + Send + Clone + Fn(crate::withdraw::server::Callback) -> WCFut,
134 WCFut: Send + Future<Output = Result<crate::CallbackResponse, StatusCode>>,
135{
136 #[allow(clippy::too_many_lines)]
137 pub fn build(self) -> Router<()> {
138 Router::new()
139 .route(
140 "/keyauth",
141 get(move |RawQuery(q): RawQuery| {
142 let ar = self.auth_request.clone();
143 async move {
144 let q = q.ok_or(StatusCode::BAD_REQUEST)?;
145 let p = q.as_str().try_into().map_err(|_| StatusCode::BAD_REQUEST)?;
146 ar(p).await.and_then(|a| {
147 Vec::<u8>::try_from(a).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
148 })
149 }
150 }),
151 )
152 .route(
153 "/lnurlc",
154 get(move || {
155 let ce = self.channel_entrypoint.clone();
156 async move {
157 ce(()).await.and_then(|a| {
158 Vec::<u8>::try_from(a).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
159 })
160 }
161 }),
162 )
163 .route(
164 "/lnurlc/callback",
165 get(move |RawQuery(q): RawQuery| {
166 let cc = self.channel_callback.clone();
167 async move {
168 let q = q.ok_or(StatusCode::BAD_REQUEST)?;
169 let p = q.as_str().try_into().map_err(|_| StatusCode::BAD_REQUEST)?;
170 cc(p).await.and_then(|a| {
171 Vec::<u8>::try_from(a).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
172 })
173 }
174 }),
175 )
176 .route(
177 "/.well-known/lnurlp/:identifier",
178 get({
179 let pe = self.pay_entrypoint.clone();
180 move |Path(identifier): Path<String>| {
181 let pe = pe.clone();
182 async move {
183 pe(Some(identifier)).await.and_then(|a| {
184 Vec::<u8>::try_from(a)
185 .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
186 })
187 }
188 }
189 }),
190 )
191 .route(
192 "/lnurlp",
193 get(move || {
194 let pe = self.pay_entrypoint.clone();
195 async move {
196 pe(None).await.and_then(|a| {
197 Vec::<u8>::try_from(a).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
198 })
199 }
200 }),
201 )
202 .route(
203 "/lnurlp/callback",
204 get(move |RawQuery(q): RawQuery| {
205 let pc = self.pay_callback.clone();
206 async move {
207 let q = q.ok_or(StatusCode::BAD_REQUEST)?;
208 let p = q.as_str().try_into().map_err(|_| StatusCode::BAD_REQUEST)?;
209 pc(p).await.map(|a| a.to_string())
210 }
211 }),
212 )
213 .route(
214 "/lnurlw",
215 get(move || {
216 let we = self.withdraw_entrypoint.clone();
217 async move {
218 we(()).await.and_then(|a| {
219 Vec::<u8>::try_from(a).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
220 })
221 }
222 }),
223 )
224 .route(
225 "/lnurlw/callback",
226 get(move |RawQuery(q): RawQuery| {
227 let wc = self.withdraw_callback.clone();
228 async move {
229 let q = q.ok_or(StatusCode::BAD_REQUEST)?;
230 let p = q.as_str().try_into().map_err(|_| StatusCode::BAD_REQUEST)?;
231 wc(p).await.and_then(|a| {
232 Vec::<u8>::try_from(a).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
233 })
234 }
235 }),
236 )
237 }
238}
239
240mod unimplemented {
241 use axum::http::StatusCode;
242 use std::{
243 future::Future,
244 marker::PhantomData,
245 pin::Pin,
246 task::{Context, Poll},
247 };
248
249 pub(super) type Handler<Param, Ret> = fn(Param) -> Unimplemented<Ret>;
250 pub(super) fn handler<Param, Ret>(_: Param) -> Unimplemented<Ret> {
251 Unimplemented(PhantomData)
252 }
253
254 pub struct Unimplemented<T>(PhantomData<T>);
255
256 impl<T> Future for Unimplemented<T> {
257 type Output = Result<T, StatusCode>;
258
259 fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<T, StatusCode>> {
260 Poll::Ready(Err(StatusCode::NOT_IMPLEMENTED))
261 }
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 #[test]
268 fn default_builds() {
269 drop(super::Server::default().build());
270 }
271}