1macro_rules! def_id_serde_impls {
2 ($struct_name:ident) => {
3 impl serde::Serialize for $struct_name {
4 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
5 where
6 S: serde::ser::Serializer,
7 {
8 self.as_str().serialize(serializer)
9 }
10 }
11
12 impl<'de> serde::Deserialize<'de> for $struct_name {
13 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
14 where
15 D: serde::de::Deserializer<'de>,
16 {
17 let s: String = serde::Deserialize::deserialize(deserializer)?;
18 s.parse::<Self>().map_err(::serde::de::Error::custom)
19 }
20 }
21 };
22 ($struct_name:ident, _) => {};
23}
24
25macro_rules! def_id {
26 ($struct_name:ident: String) => {
27 #[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
28 pub struct $struct_name(smol_str::SmolStr);
29
30 impl $struct_name {
31 #[inline(always)]
33 pub fn as_str(&self) -> &str {
34 self.0.as_str()
35 }
36 }
37
38 impl PartialEq<str> for $struct_name {
39 fn eq(&self, other: &str) -> bool {
40 self.as_str() == other
41 }
42 }
43
44 impl PartialEq<&str> for $struct_name {
45 fn eq(&self, other: &&str) -> bool {
46 self.as_str() == *other
47 }
48 }
49
50 impl PartialEq<String> for $struct_name {
51 fn eq(&self, other: &String) -> bool {
52 self.as_str() == other
53 }
54 }
55
56 impl PartialOrd for $struct_name {
57 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
58 Some(self.cmp(other))
59 }
60 }
61
62 impl Ord for $struct_name {
63 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
64 self.as_str().cmp(other.as_str())
65 }
66 }
67
68 impl AsRef<str> for $struct_name {
69 fn as_ref(&self) -> &str {
70 self.as_str()
71 }
72 }
73
74 impl std::ops::Deref for $struct_name {
75 type Target = str;
76
77 fn deref(&self) -> &str {
78 self.as_str()
79 }
80 }
81
82 impl std::fmt::Display for $struct_name {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 self.0.fmt(f)
85 }
86 }
87
88 impl std::str::FromStr for $struct_name {
89 type Err = ParseIdError;
90
91 fn from_str(s: &str) -> Result<Self, Self::Err> {
92 Ok($struct_name(s.into()))
93 }
94 }
95
96 impl serde::Serialize for $struct_name {
97 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98 where S: serde::ser::Serializer
99 {
100 self.as_str().serialize(serializer)
101 }
102 }
103
104 impl<'de> serde::Deserialize<'de> for $struct_name {
105 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
106 where D: serde::de::Deserializer<'de>
107 {
108 let s: String = serde::Deserialize::deserialize(deserializer)?;
109 s.parse::<Self>().map_err(::serde::de::Error::custom)
110 }
111 }
112 };
113 ($struct_name:ident, $prefix:literal $(| $alt_prefix:literal)* $(, { $generate_hint:tt })?) => {
114 #[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
119 pub struct $struct_name(smol_str::SmolStr);
120
121 impl $struct_name {
122 #[inline(always)]
124 #[deprecated(note = "Please use prefixes or is_valid_prefix")]
125 pub fn prefix() -> &'static str {
126 $prefix
127 }
128
129 #[inline(always)]
131 pub fn prefixes() -> &'static [&'static str] {
132 &[$prefix$(, $alt_prefix)*]
133 }
134
135 #[inline(always)]
137 pub fn as_str(&self) -> &str {
138 self.0.as_str()
139 }
140
141 pub fn is_valid_prefix(prefix: &str) -> bool {
143 prefix == $prefix $( || prefix == $alt_prefix )*
144 }
145 }
146
147 impl PartialEq<str> for $struct_name {
148 fn eq(&self, other: &str) -> bool {
149 self.as_str() == other
150 }
151 }
152
153 impl PartialEq<&str> for $struct_name {
154 fn eq(&self, other: &&str) -> bool {
155 self.as_str() == *other
156 }
157 }
158
159 impl PartialEq<String> for $struct_name {
160 fn eq(&self, other: &String) -> bool {
161 self.as_str() == other
162 }
163 }
164
165 impl PartialOrd for $struct_name {
166 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
167 Some(self.cmp(other))
168 }
169 }
170
171 impl Ord for $struct_name {
172 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
173 self.as_str().cmp(other.as_str())
174 }
175 }
176
177 impl AsRef<str> for $struct_name {
178 fn as_ref(&self) -> &str {
179 self.as_str()
180 }
181 }
182
183 impl std::ops::Deref for $struct_name {
184 type Target = str;
185
186 fn deref(&self) -> &str {
187 self.as_str()
188 }
189 }
190
191 impl std::fmt::Display for $struct_name {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 self.0.fmt(f)
194 }
195 }
196
197 impl std::str::FromStr for $struct_name {
198 type Err = ParseIdError;
199
200 fn from_str(s: &str) -> Result<Self, Self::Err> {
201 if !s.starts_with($prefix) $(
202 && !s.starts_with($alt_prefix)
203 )* {
204 eprintln!("bad id is: {} (expected: {:?}) for {}", s, $prefix, stringify!($struct_name));
206
207 Err(ParseIdError {
208 typename: stringify!($struct_name),
209 expected: stringify!(id to start with $prefix $(or $alt_prefix)*),
210 })
211 } else {
212 Ok($struct_name(s.into()))
213 }
214 }
215 }
216
217 def_id_serde_impls!($struct_name $(, $generate_hint )*);
218 };
219 (#[optional] enum $enum_name:ident { $( $variant_name:ident($($variant_type:tt)*) ),* $(,)* }) => {
220 #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
221 pub enum $enum_name {
222 None,
223 $( $variant_name($($variant_type)*), )*
224 }
225
226 impl $enum_name {
227 pub fn as_str(&self) -> &str {
228 match *self {
229 $enum_name::None => "",
230 $( $enum_name::$variant_name(ref id) => id.as_str(), )*
231 }
232 }
233 }
234
235 impl PartialEq<str> for $enum_name {
236 fn eq(&self, other: &str) -> bool {
237 self.as_str() == other
238 }
239 }
240
241 impl PartialEq<&str> for $enum_name {
242 fn eq(&self, other: &&str) -> bool {
243 self.as_str() == *other
244 }
245 }
246
247 impl PartialEq<String> for $enum_name {
248 fn eq(&self, other: &String) -> bool {
249 self.as_str() == other
250 }
251 }
252
253 impl AsRef<str> for $enum_name {
254 fn as_ref(&self) -> &str {
255 self.as_str()
256 }
257 }
258
259 impl crate::params::AsCursor for $enum_name {}
260
261 impl std::ops::Deref for $enum_name {
262 type Target = str;
263
264 fn deref(&self) -> &str {
265 self.as_str()
266 }
267 }
268
269 impl std::fmt::Display for $enum_name {
270 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
271 match *self {
272 $enum_name::None => Ok(()),
273 $( $enum_name::$variant_name(ref id) => id.fmt(f), )*
274 }
275 }
276 }
277
278 impl std::default::Default for $enum_name {
279 fn default() -> Self {
280 $enum_name::None
281 }
282 }
283
284 impl std::str::FromStr for $enum_name {
285 type Err = ParseIdError;
286
287 fn from_str(s: &str) -> Result<Self, Self::Err> {
288 let prefix = s.find('_')
289 .map(|i| &s[0..=i])
290 .ok_or_else(|| ParseIdError {
291 typename: stringify!($enum_name),
292 expected: "id to start with a prefix (as in 'prefix_')"
293 })?;
294
295 match prefix {
296 $(_ if $($variant_type)*::is_valid_prefix(prefix) => {
297 Ok($enum_name::$variant_name(s.parse()?))
298 })*
299 _ => {
300 Err(ParseIdError {
301 typename: stringify!($enum_name),
302 expected: "unknown id prefix",
303 })
304 }
305 }
306 }
307 }
308
309 impl serde::Serialize for $enum_name {
310 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
311 where S: serde::ser::Serializer
312 {
313 self.as_str().serialize(serializer)
314 }
315 }
316
317 impl<'de> serde::Deserialize<'de> for $enum_name {
318 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
319 where D: serde::de::Deserializer<'de>
320 {
321 let s: String = serde::Deserialize::deserialize(deserializer)?;
322 s.parse::<Self>().map_err(::serde::de::Error::custom)
323 }
324 }
325
326 $(
327 impl From<$($variant_type)*> for $enum_name {
328 fn from(id: $($variant_type)*) -> Self {
329 $enum_name::$variant_name(id)
330 }
331 }
332 )*
333 };
334 (enum $enum_name:ident { $( $(#[$test:meta])? $variant_name:ident($($variant_type:tt)*) ),+ $(,)? }) => {
335 #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
336 #[derive(SmartDefault)]
337 pub enum $enum_name {
338 $( $(#[$test])* $variant_name($($variant_type)*), )*
339 }
340
341 impl $enum_name {
342 pub fn as_str(&self) -> &str {
343 match *self {
344 $( $enum_name::$variant_name(ref id) => id.as_str(), )*
345 }
346 }
347 }
348
349 impl PartialEq<str> for $enum_name {
350 fn eq(&self, other: &str) -> bool {
351 self.as_str() == other
352 }
353 }
354
355 impl PartialEq<&str> for $enum_name {
356 fn eq(&self, other: &&str) -> bool {
357 self.as_str() == *other
358 }
359 }
360
361 impl PartialEq<String> for $enum_name {
362 fn eq(&self, other: &String) -> bool {
363 self.as_str() == other
364 }
365 }
366
367 impl AsRef<str> for $enum_name {
368 fn as_ref(&self) -> &str {
369 self.as_str()
370 }
371 }
372
373 impl crate::params::AsCursor for $enum_name {}
374
375 impl std::ops::Deref for $enum_name {
376 type Target = str;
377
378 fn deref(&self) -> &str {
379 self.as_str()
380 }
381 }
382
383 impl std::fmt::Display for $enum_name {
384 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
385 match *self {
386 $( $enum_name::$variant_name(ref id) => id.fmt(f), )*
387 }
388 }
389 }
390
391 impl std::str::FromStr for $enum_name {
392 type Err = ParseIdError;
393
394 fn from_str(s: &str) -> Result<Self, Self::Err> {
395 let prefix = s.find('_')
396 .map(|i| &s[0..=i])
397 .ok_or_else(|| ParseIdError {
398 typename: stringify!($enum_name),
399 expected: "id to start with a prefix (as in 'prefix_')"
400 })?;
401
402 match prefix {
403 $(_ if $($variant_type)*::is_valid_prefix(prefix) => {
404 Ok($enum_name::$variant_name(s.parse()?))
405 })*
406 _ => {
407 Err(ParseIdError {
408 typename: stringify!($enum_name),
409 expected: "unknown id prefix",
410 })
411 }
412 }
413 }
414 }
415
416 impl serde::Serialize for $enum_name {
417 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
418 where S: serde::ser::Serializer
419 {
420 self.as_str().serialize(serializer)
421 }
422 }
423
424 impl<'de> serde::Deserialize<'de> for $enum_name {
425 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
426 where D: serde::de::Deserializer<'de>
427 {
428 let s: String = serde::Deserialize::deserialize(deserializer)?;
429 s.parse::<Self>().map_err(::serde::de::Error::custom)
430 }
431 }
432
433 $(
434 impl From<$($variant_type)*> for $enum_name {
435 fn from(id: $($variant_type)*) -> Self {
436 $enum_name::$variant_name(id)
437 }
438 }
439 )*
440 };
441}
442
443#[derive(Clone, Debug)]
444pub struct ParseIdError {
445 typename: &'static str,
446 expected: &'static str,
447}
448
449impl std::fmt::Display for ParseIdError {
450 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451 write!(f, "invalid `{}`, expected {}", self.typename, self.expected)
452 }
453}
454
455impl std::error::Error for ParseIdError {
456 fn description(&self) -> &str {
457 "error parsing an id"
458 }
459}
460
461def_id!(AuthorisationId, "auth_");
462def_id!(ApplePayId, "apmd_");
463def_id!(BankAccountId, "ba_");
464def_id!(CardId, "card_");
465def_id!(ChargeId, "ch_");
466def_id!(CustomerId, "cus_");
467def_id!(DepositId, "dpo_");
468def_id!(DisputeId, "dis_");
469def_id!(EventId, "evt_");
470def_id!(FileId, "file_");
471def_id!(MerchantId, "mrch_");
472def_id!(PaymentSourceId, "ps_");
473def_id!(PlanId, "plan_");
474def_id!(RecipientId, "rp_");
475def_id!(RefundId, "rf_");
476def_id!(SessionId, "se_");
477def_id!(SubscriptionId, "sub_");
478def_id!(TransferId, "tfer_");
479def_id!(WebhookEndpointId, "whe_");
480def_id!(WebhookId, "whr_");
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485
486 #[test]
487 fn test_parse_customer() {
488 assert!("cus_123".parse::<CustomerId>().is_ok());
489 let bad_parse = "zzz_123".parse::<CustomerId>();
490 assert!(bad_parse.is_err());
491 if let Err(err) = bad_parse {
492 assert_eq!(
493 format!("{}", err),
494 "invalid `CustomerId`, expected id to start with \"cus_\""
495 );
496 }
497 }
498
499 #[test]
500 fn test_parse_charge() {
501 assert!("ch_123".parse::<ChargeId>().is_ok());
502 let bad_parse = "zz_123".parse::<ChargeId>();
503 assert!(bad_parse.is_err());
504 if let Err(err) = bad_parse {
505 assert_eq!(
506 format!("{}", err),
507 "invalid `ChargeId`, expected id to start with \"ch_\""
508 );
509 }
510 }
511
512 #[test]
513 fn test_parse_session() {
514 assert!("se_123".parse::<SessionId>().is_ok());
515 let bad_parse = "zz_123".parse::<SessionId>();
516 assert!(bad_parse.is_err());
517 if let Err(err) = bad_parse {
518 assert_eq!(
519 format!("{}", err),
520 "invalid `SessionId`, expected id to start with \"se_\""
521 );
522 }
523 }
524}