odos_sdk/types/
referral.rs

1// SPDX-FileCopyrightText: 2025 Semiotic AI, Inc.
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use std::fmt;
6
7use serde::{Deserialize, Serialize};
8
9/// Type-safe referral code
10///
11/// Provides a clear, type-safe wrapper around referral codes with
12/// convenient constants for common values.
13///
14/// # Examples
15///
16/// ```rust
17/// use odos_sdk::ReferralCode;
18///
19/// // No referral code
20/// let code = ReferralCode::NONE;
21/// assert_eq!(code.code(), 0);
22///
23/// // Custom referral code
24/// let code = ReferralCode::new(42);
25/// assert_eq!(code.code(), 42);
26/// ```
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
28#[serde(transparent)]
29pub struct ReferralCode(u32);
30
31impl ReferralCode {
32    /// No referral code (value: 0)
33    ///
34    /// This is the most common case - no referral program participation.
35    ///
36    /// # Examples
37    ///
38    /// ```rust
39    /// use odos_sdk::ReferralCode;
40    ///
41    /// let code = ReferralCode::NONE;
42    /// assert_eq!(code.code(), 0);
43    /// ```
44    pub const NONE: Self = Self(0);
45
46    /// Create a new referral code
47    ///
48    /// # Arguments
49    ///
50    /// * `code` - The referral code value
51    ///
52    /// # Examples
53    ///
54    /// ```rust
55    /// use odos_sdk::ReferralCode;
56    ///
57    /// let code = ReferralCode::new(42);
58    /// assert_eq!(code.code(), 42);
59    ///
60    /// let code = ReferralCode::new(1234);
61    /// assert_eq!(code.code(), 1234);
62    /// ```
63    pub const fn new(code: u32) -> Self {
64        Self(code)
65    }
66
67    /// Get the referral code value
68    ///
69    /// # Examples
70    ///
71    /// ```rust
72    /// use odos_sdk::ReferralCode;
73    ///
74    /// let code = ReferralCode::new(42);
75    /// assert_eq!(code.code(), 42);
76    /// ```
77    pub const fn code(&self) -> u32 {
78        self.0
79    }
80
81    /// Check if this is the NONE referral code
82    ///
83    /// # Examples
84    ///
85    /// ```rust
86    /// use odos_sdk::ReferralCode;
87    ///
88    /// assert!(ReferralCode::NONE.is_none());
89    /// assert!(!ReferralCode::new(42).is_none());
90    /// ```
91    pub const fn is_none(&self) -> bool {
92        self.0 == 0
93    }
94
95    /// Check if this is a valid referral code (non-zero)
96    ///
97    /// # Examples
98    ///
99    /// ```rust
100    /// use odos_sdk::ReferralCode;
101    ///
102    /// assert!(!ReferralCode::NONE.is_some());
103    /// assert!(ReferralCode::new(42).is_some());
104    /// ```
105    pub const fn is_some(&self) -> bool {
106        self.0 != 0
107    }
108}
109
110impl Default for ReferralCode {
111    /// Default referral code is NONE (0)
112    fn default() -> Self {
113        Self::NONE
114    }
115}
116
117impl fmt::Display for ReferralCode {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        if self.is_none() {
120            write!(f, "None")
121        } else {
122            write!(f, "{}", self.0)
123        }
124    }
125}
126
127impl From<ReferralCode> for u32 {
128    fn from(code: ReferralCode) -> Self {
129        code.0
130    }
131}
132
133impl From<u32> for ReferralCode {
134    fn from(code: u32) -> Self {
135        Self(code)
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_none_constant() {
145        assert_eq!(ReferralCode::NONE.code(), 0);
146        assert!(ReferralCode::NONE.is_none());
147        assert!(!ReferralCode::NONE.is_some());
148    }
149
150    #[test]
151    fn test_new() {
152        let code = ReferralCode::new(42);
153        assert_eq!(code.code(), 42);
154        assert!(!code.is_none());
155        assert!(code.is_some());
156
157        let code = ReferralCode::new(0);
158        assert_eq!(code.code(), 0);
159        assert!(code.is_none());
160        assert!(!code.is_some());
161
162        let code = ReferralCode::new(u32::MAX);
163        assert_eq!(code.code(), u32::MAX);
164        assert!(code.is_some());
165    }
166
167    #[test]
168    fn test_default() {
169        let code = ReferralCode::default();
170        assert_eq!(code, ReferralCode::NONE);
171        assert_eq!(code.code(), 0);
172    }
173
174    #[test]
175    fn test_display() {
176        assert_eq!(format!("{}", ReferralCode::NONE), "None");
177        assert_eq!(format!("{}", ReferralCode::new(42)), "42");
178        assert_eq!(format!("{}", ReferralCode::new(1234)), "1234");
179    }
180
181    #[test]
182    fn test_conversions() {
183        // From ReferralCode to u32
184        let code = ReferralCode::new(42);
185        let value: u32 = code.into();
186        assert_eq!(value, 42);
187
188        // From u32 to ReferralCode
189        let code: ReferralCode = 42u32.into();
190        assert_eq!(code.code(), 42);
191    }
192
193    #[test]
194    fn test_equality() {
195        assert_eq!(ReferralCode::new(42), ReferralCode::new(42));
196        assert_ne!(ReferralCode::new(42), ReferralCode::new(43));
197        assert_eq!(ReferralCode::NONE, ReferralCode::new(0));
198    }
199
200    #[test]
201    fn test_ordering() {
202        assert!(ReferralCode::new(1) < ReferralCode::new(2));
203        assert!(ReferralCode::new(100) > ReferralCode::new(50));
204        assert!(ReferralCode::NONE < ReferralCode::new(1));
205    }
206
207    #[test]
208    fn test_serialization() {
209        let code = ReferralCode::new(42);
210
211        // Serialize
212        let json = serde_json::to_string(&code).unwrap();
213        assert_eq!(json, "42");
214
215        // Deserialize
216        let deserialized: ReferralCode = serde_json::from_str(&json).unwrap();
217        assert_eq!(deserialized.code(), 42);
218
219        // Serialize NONE
220        let json = serde_json::to_string(&ReferralCode::NONE).unwrap();
221        assert_eq!(json, "0");
222    }
223}