1use std::fmt;
4
5use reqwest::header::{AUTHORIZATION, HeaderMap, HeaderValue};
6use serde::{Deserialize, Serialize};
7
8use crate::{Result, TxlineError};
9
10pub const API_TOKEN_HEADER: &str = "X-Api-Token";
11
12#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct GuestJwt(String);
14
15impl GuestJwt {
16 pub fn new(token: impl Into<String>) -> Result<Self> {
20 let token = token.into().trim().to_owned();
21 if token.is_empty() {
22 return Err(TxlineError::invalid_input("guest JWT must not be empty"));
23 }
24 Ok(Self(token))
25 }
26
27 pub fn as_str(&self) -> &str {
28 &self.0
29 }
30}
31
32impl fmt::Debug for GuestJwt {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 f.write_str("GuestJwt(<redacted>)")
35 }
36}
37
38#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub struct ApiToken(String);
40
41impl ApiToken {
42 pub fn new(token: impl Into<String>) -> Result<Self> {
46 let token = token.into().trim().to_owned();
47 if token.is_empty() {
48 return Err(TxlineError::invalid_input("API token must not be empty"));
49 }
50 Ok(Self(token))
51 }
52
53 pub fn as_str(&self) -> &str {
54 &self.0
55 }
56}
57
58impl fmt::Debug for ApiToken {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 f.write_str("ApiToken(<redacted>)")
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct GuestSession {
66 pub token: GuestJwt,
67}
68
69#[derive(Clone, PartialEq, Eq)]
70pub struct AuthHeaders {
71 authorization: GuestJwt,
72 api_token: Option<ApiToken>,
73}
74
75impl AuthHeaders {
76 pub fn new(authorization: GuestJwt, api_token: Option<ApiToken>) -> Self {
77 Self {
78 authorization,
79 api_token,
80 }
81 }
82
83 pub fn to_header_map(&self) -> Result<HeaderMap> {
84 let mut headers = HeaderMap::new();
85 let auth_value = format!("Bearer {}", self.authorization.as_str());
86 headers.insert(AUTHORIZATION, HeaderValue::from_str(&auth_value)?);
87 if let Some(api_token) = &self.api_token {
88 headers.insert(API_TOKEN_HEADER, HeaderValue::from_str(api_token.as_str())?);
89 }
90 Ok(headers)
91 }
92
93 pub fn has_api_token(&self) -> bool {
94 self.api_token.is_some()
95 }
96}
97
98impl fmt::Debug for AuthHeaders {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("AuthHeaders")
101 .field("authorization", &"<redacted>")
102 .field("api_token", &self.api_token.as_ref().map(|_| "<redacted>"))
103 .finish()
104 }
105}
106
107#[derive(Debug, Deserialize)]
108pub(crate) struct TokenResponse {
109 pub token: String,
110}
111
112#[derive(Debug, Serialize)]
113pub(crate) struct ActivationPayload<'a> {
114 #[serde(rename = "txSig")]
115 pub tx_sig: &'a str,
116 #[serde(rename = "walletSignature")]
117 pub wallet_signature: &'a str,
118 pub leagues: &'a [i32],
119}
120
121pub fn activation_preimage(
125 tx_sig: impl AsRef<str>,
126 selected_leagues: &[i32],
127 jwt: &GuestJwt,
128) -> String {
129 let leagues = selected_leagues
130 .iter()
131 .map(i32::to_string)
132 .collect::<Vec<_>>()
133 .join(",");
134 format!("{}:{}:{}", tx_sig.as_ref(), leagues, jwt.as_str())
135}