sure_client_rs/
types.rs

1use serde::{Deserialize, Serialize};
2use std::fmt::{self, Display};
3use std::ops::Deref;
4use uuid::Uuid;
5
6/// Macro to create a simple newtype wrapper for strings
7macro_rules! newtype_string {
8    ($(#[$attr:meta])* $vis:vis $name:ident) => {
9        $(#[$attr])*
10        #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11        #[serde(transparent)]
12        $vis struct $name(String);
13
14        impl $name {
15            /// Create a new instance
16            pub fn new<T: Into<String>>(value: T) -> Self {
17                Self(value.into())
18            }
19
20            /// Get the inner string as a str
21            pub fn as_str(&self) -> &str {
22                &self.0
23            }
24        }
25
26        impl Deref for $name {
27            type Target = str;
28            fn deref(&self) -> &Self::Target {
29                &self.0
30            }
31        }
32
33        impl Display for $name {
34            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35                write!(f, "{}", self.0)
36            }
37        }
38
39        impl AsRef<str> for $name {
40            fn as_ref(&self) -> &str {
41                &self.0
42            }
43        }
44    };
45}
46
47/// Macro to create a newtype wrapper for UUIDs
48macro_rules! newtype_uuid {
49    ($(#[$attr:meta])* $vis:vis $name:ident) => {
50        $(#[$attr])*
51        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
52        #[serde(transparent)]
53        $vis struct $name(Uuid);
54
55        impl $name {
56            /// Create a new instance from a UUID
57            pub const fn new(value: Uuid) -> Self {
58                Self(value)
59            }
60
61            /// Parse from a string
62            pub fn parse(value: &str) -> Result<Self, uuid::Error> {
63                Ok(Self(Uuid::parse_str(value)?))
64            }
65
66            /// Get the inner UUID
67            pub const fn as_uuid(&self) -> &Uuid {
68                &self.0
69            }
70        }
71
72        impl Display for $name {
73            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74                write!(f, "{}", self.0)
75            }
76        }
77
78        impl From<Uuid> for $name {
79            fn from(uuid: Uuid) -> Self {
80                Self(uuid)
81            }
82        }
83    };
84}
85
86newtype_string!(
87    /// Bearer token for authentication (JWT)
88    pub BearerToken
89);
90
91newtype_string!(
92    /// API key for authentication via X-Api-Key header
93    pub ApiKey
94);
95
96/// Authentication method for the Sure API
97///
98/// The API supports two authentication methods:
99/// - Bearer token (JWT) via Authorization header
100/// - API key via X-Api-Key header
101#[derive(Debug, Clone, PartialEq, Eq)]
102pub enum Auth {
103    /// Bearer token authentication (Authorization: Bearer \<token\>)
104    Bearer(BearerToken),
105    /// API key authentication (X-Api-Key: \<key\>)
106    ApiKey(ApiKey),
107}
108
109impl Auth {
110    /// Create Bearer token authentication
111    pub fn bearer<T: Into<String>>(token: T) -> Self {
112        Self::Bearer(BearerToken::new(token))
113    }
114
115    /// Create API key authentication
116    pub fn api_key<T: Into<String>>(key: T) -> Self {
117        Self::ApiKey(ApiKey::new(key))
118    }
119}
120
121impl From<BearerToken> for Auth {
122    fn from(token: BearerToken) -> Self {
123        Self::Bearer(token)
124    }
125}
126
127impl From<ApiKey> for Auth {
128    fn from(key: ApiKey) -> Self {
129        Self::ApiKey(key)
130    }
131}
132
133newtype_uuid!(
134    /// Account identifier
135    pub AccountId
136);
137
138newtype_uuid!(
139    /// Category identifier
140    pub CategoryId
141);
142
143newtype_uuid!(
144    /// Merchant identifier
145    pub MerchantId
146);
147
148newtype_uuid!(
149    /// Tag identifier
150    pub TagId
151);
152
153newtype_uuid!(
154    /// Transaction identifier
155    pub TransactionId
156);
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_bearer_token() {
164        let token = BearerToken::new("test_token");
165        assert_eq!(token.as_str(), "test_token");
166        assert_eq!(&*token, "test_token");
167        assert_eq!(token.to_string(), "test_token");
168    }
169
170    #[test]
171    fn test_uuid_types() {
172        let uuid = Uuid::new_v4();
173        let account_id = AccountId::new(uuid);
174        assert_eq!(account_id.as_uuid(), &uuid);
175        assert_eq!(account_id.to_string(), uuid.to_string());
176
177        let parsed = AccountId::parse(&uuid.to_string())
178            .expect("UUID parsing should succeed for valid UUID string");
179        assert_eq!(parsed, account_id);
180    }
181}