Skip to main content

hinge_rs/
enums.rs

1use serde::{Deserialize, Serialize};
2
3/// Module containing serializers for API format (numeric enums)
4pub mod api_format {
5    use serde::{Serialize, Serializer};
6
7    /// Serialize an enum to its numeric value for the API
8    pub fn serialize_enum<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
9    where
10        S: Serializer,
11        T: super::ApiEnum,
12    {
13        serializer.serialize_i8(value.to_api_value())
14    }
15
16    /// Serialize an optional enum to its numeric value for the API
17    pub fn serialize_option_enum<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: Serializer,
20        T: super::ApiEnum,
21    {
22        match value {
23            Some(v) => serializer.serialize_i8(v.to_api_value()),
24            None => serializer.serialize_none(),
25        }
26    }
27
28    /// Serialize a vector of enums to their numeric values for the API
29    pub fn serialize_vec_enum<S, T>(values: &[T], serializer: S) -> Result<S::Ok, S::Error>
30    where
31        S: Serializer,
32        T: super::ApiEnum,
33    {
34        let nums: Vec<i8> = values.iter().map(|e| e.to_api_value()).collect();
35        nums.serialize(serializer)
36    }
37}
38
39/// Trait for enums that can be converted to API numeric values
40pub trait ApiEnum: Sized {
41    fn to_api_value(&self) -> i8;
42    fn from_string(s: &str) -> Option<Self>;
43}
44
45// Macro to implement both numeric and string ser/de
46macro_rules! impl_numeric_string_enum {
47    ($enum_name:ident { $($variant:ident = $value:expr),* $(,)? }) => {
48        #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
49        #[derive(Clone, Debug, PartialEq)]
50        pub enum $enum_name {
51            $($variant,)*
52        }
53
54        impl $enum_name {
55            /// Return the static string representation of the enum variant
56            pub fn as_str(&self) -> &'static str {
57                match self {
58                    $(Self::$variant => stringify!($variant),)*
59                }
60            }
61        }
62
63        impl Serialize for $enum_name {
64            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65            where
66                S: serde::Serializer,
67            {
68                // Always serialize as strings for TypeScript compatibility
69                // The conversion to numbers happens in the API client
70                let s = match self {
71                    $(Self::$variant => stringify!($variant),)*
72                };
73                serializer.serialize_str(s)
74            }
75        }
76
77        impl<'de> Deserialize<'de> for $enum_name {
78            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79            where
80                D: serde::Deserializer<'de>,
81            {
82                struct Visitor;
83
84                impl<'de> serde::de::Visitor<'de> for Visitor {
85                    type Value = $enum_name;
86
87                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
88                        formatter.write_str(concat!("a valid ", stringify!($enum_name), " value"))
89                    }
90
91                    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
92                    where
93                        E: serde::de::Error,
94                    {
95                        match value {
96                            $($value => Ok($enum_name::$variant),)*
97                            _ => Err(E::custom(format!("invalid {} value: {}", stringify!($enum_name), value)))
98                        }
99                    }
100
101                    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
102                    where
103                        E: serde::de::Error,
104                    {
105                        self.visit_i64(value as i64)
106                    }
107
108                    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
109                    where
110                        E: serde::de::Error,
111                    {
112                        match value {
113                            $(stringify!($variant) => Ok($enum_name::$variant),)*
114                            _ => Err(E::custom(format!("invalid {} value: {}", stringify!($enum_name), value)))
115                        }
116                    }
117                }
118
119                deserializer.deserialize_any(Visitor)
120            }
121        }
122
123        impl ApiEnum for $enum_name {
124            fn to_api_value(&self) -> i8 {
125                match self {
126                    $(Self::$variant => $value,)*
127                }
128            }
129
130            fn from_string(s: &str) -> Option<Self> {
131                match s {
132                    $(stringify!($variant) => Some(Self::$variant),)*
133                    _ => None
134                }
135            }
136        }
137    };
138}
139
140macro_rules! impl_profile_preference_enum {
141    ($base:ident, $profile:ident, $preference:ident { $($variant:ident = $value:expr),* $(,)? }) => {
142        impl_numeric_string_enum! {
143            $base {
144                $($variant = $value,)*
145            }
146        }
147
148        #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
149        #[derive(Clone, Debug, PartialEq)]
150        pub enum $profile {
151            PreferNotToSay,
152            $($variant,)*
153        }
154
155        impl $profile {
156            /// Returns the base value for this profile option, if one exists.
157            pub fn to_value(&self) -> Option<$base> {
158                match self {
159                    Self::PreferNotToSay => None,
160                    $(Self::$variant => Some($base::$variant),)*
161                }
162            }
163
164            /// Construct a profile value from a shared base value.
165            pub fn from_value(value: $base) -> Self {
166                match value {
167                    $($base::$variant => Self::$variant,)*
168                }
169            }
170
171            /// Convert this profile value into a preference variant when possible.
172            pub fn into_preference(self) -> Option<$preference> {
173                self.to_value().map($preference::from_value)
174            }
175        }
176
177        impl From<$base> for $profile {
178            fn from(value: $base) -> Self {
179                Self::from_value(value)
180            }
181        }
182
183        impl Serialize for $profile {
184            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
185            where
186                S: serde::Serializer,
187            {
188                match self {
189                    Self::PreferNotToSay => serializer.serialize_str("PreferNotToSay"),
190                    $(Self::$variant => serializer.serialize_str(stringify!($variant)),)*
191                }
192            }
193        }
194
195        impl<'de> Deserialize<'de> for $profile {
196            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
197            where
198                D: serde::Deserializer<'de>,
199            {
200                struct Visitor;
201
202                impl<'de> serde::de::Visitor<'de> for Visitor {
203                    type Value = $profile;
204
205                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
206                        formatter.write_str(concat!("a valid ", stringify!($profile), " value"))
207                    }
208
209                    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
210                    where
211                        E: serde::de::Error,
212                    {
213                        match value {
214                            0 => Ok($profile::PreferNotToSay),
215                            $($value => Ok($profile::$variant),)*
216                            _ => Err(E::custom(format!("invalid {} value: {}", stringify!($profile), value))),
217                        }
218                    }
219
220                    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
221                    where
222                        E: serde::de::Error,
223                    {
224                        self.visit_i64(value as i64)
225                    }
226
227                    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
228                    where
229                        E: serde::de::Error,
230                    {
231                        match value {
232                            "PreferNotToSay" => Ok($profile::PreferNotToSay),
233                            $(stringify!($variant) => Ok($profile::$variant),)*
234                            _ => Err(E::custom(format!("invalid {} value: {}", stringify!($profile), value))),
235                        }
236                    }
237                }
238
239                deserializer.deserialize_any(Visitor)
240            }
241        }
242
243        impl ApiEnum for $profile {
244            fn to_api_value(&self) -> i8 {
245                match self {
246                    Self::PreferNotToSay => 0,
247                    $(Self::$variant => $value,)*
248                }
249            }
250
251            fn from_string(s: &str) -> Option<Self> {
252                match s {
253                    "PreferNotToSay" => Some(Self::PreferNotToSay),
254                    $(stringify!($variant) => Some(Self::$variant),)*
255                    _ => None,
256                }
257            }
258        }
259
260        #[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
261        #[derive(Clone, Debug, PartialEq)]
262        pub enum $preference {
263            OpenToAll,
264            $($variant,)*
265        }
266
267        impl $preference {
268            /// Returns the base value for this preference option, if one exists.
269            pub fn to_value(&self) -> Option<$base> {
270                match self {
271                    Self::OpenToAll => None,
272                    $(Self::$variant => Some($base::$variant),)*
273                }
274            }
275
276            /// Construct a preference value from a shared base value.
277            pub fn from_value(value: $base) -> Self {
278                match value {
279                    $($base::$variant => Self::$variant,)*
280                }
281            }
282
283            /// Convert this preference value into a profile variant when possible.
284            pub fn into_profile(self) -> Option<$profile> {
285                self.to_value().map($profile::from_value)
286            }
287        }
288
289        impl From<$base> for $preference {
290            fn from(value: $base) -> Self {
291                Self::from_value(value)
292            }
293        }
294
295        impl Serialize for $preference {
296            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
297            where
298                S: serde::Serializer,
299            {
300                match self {
301                    Self::OpenToAll => serializer.serialize_str("OpenToAll"),
302                    $(Self::$variant => serializer.serialize_str(stringify!($variant)),)*
303                }
304            }
305        }
306
307        impl<'de> Deserialize<'de> for $preference {
308            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
309            where
310                D: serde::Deserializer<'de>,
311            {
312                struct Visitor;
313
314                impl<'de> serde::de::Visitor<'de> for Visitor {
315                    type Value = $preference;
316
317                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
318                        formatter.write_str(concat!("a valid ", stringify!($preference), " value"))
319                    }
320
321                    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
322                    where
323                        E: serde::de::Error,
324                    {
325                        match value {
326                            -1 => Ok($preference::OpenToAll),
327                            $($value => Ok($preference::$variant),)*
328                            _ => Err(E::custom(format!("invalid {} value: {}", stringify!($preference), value))),
329                        }
330                    }
331
332                    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
333                    where
334                        E: serde::de::Error,
335                    {
336                        self.visit_i64(value as i64)
337                    }
338
339                    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
340                    where
341                        E: serde::de::Error,
342                    {
343                        match value {
344                            "OpenToAll" => Ok($preference::OpenToAll),
345                            $(stringify!($variant) => Ok($preference::$variant),)*
346                            _ => Err(E::custom(format!("invalid {} value: {}", stringify!($preference), value))),
347                        }
348                    }
349                }
350
351                deserializer.deserialize_any(Visitor)
352            }
353        }
354
355        impl ApiEnum for $preference {
356            fn to_api_value(&self) -> i8 {
357                match self {
358                    Self::OpenToAll => -1,
359                    $(Self::$variant => $value,)*
360                }
361            }
362
363            fn from_string(s: &str) -> Option<Self> {
364                match s {
365                    "OpenToAll" => Some(Self::OpenToAll),
366                    $(stringify!($variant) => Some(Self::$variant),)*
367                    _ => None,
368                }
369            }
370        }
371    };
372}
373
374#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
375#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
376#[serde(rename_all = "lowercase")]
377pub enum ContentType {
378    Text,
379    Media,
380    Audio,
381    Video,
382    Voice,
383    Poll,
384}
385
386impl_profile_preference_enum! {
387    ChildrenStatusValue,
388    ChildrenStatusProfile,
389    ChildrenStatusPreference {
390        No = 1,
391        Yes = 2,
392    }
393}
394
395impl_profile_preference_enum! {
396    DatingIntentionValue,
397    DatingIntentionProfile,
398    DatingIntentionPreference {
399        LifePartner = 1,
400        LongTermRelationship = 2,
401        LongTermOpenToShort = 3,
402        ShortTermOpenToLong = 4,
403        ShortTermRelationship = 5,
404        FiguringOutTheirDatingGoals = 6,
405    }
406}
407
408impl_profile_preference_enum! {
409    DrinkingStatusValue,
410    DrinkingStatusProfile,
411    DrinkingStatusPreference {
412        No = 1,
413        Yes = 2,
414        Sometimes = 3,
415    }
416}
417
418impl_profile_preference_enum! {
419    SmokingStatusValue,
420    SmokingStatusProfile,
421    SmokingStatusPreference {
422        No = 1,
423        Yes = 2,
424        Sometimes = 3,
425    }
426}
427
428impl_profile_preference_enum! {
429    MarijuanaStatusValue,
430    MarijuanaStatusProfile,
431    MarijuanaStatusPreference {
432        No = 1,
433        Yes = 2,
434        Sometimes = 3,
435        NoPreference = 4,
436    }
437}
438
439impl_profile_preference_enum! {
440    DrugStatusValue,
441    DrugStatusProfile,
442    DrugStatusPreference {
443        No = 1,
444        Yes = 2,
445        Sometimes = 3,
446    }
447}
448
449impl_numeric_string_enum! {
450    GenderEnum {
451        Man = 0,
452        Woman = 1,
453        NonBinary = 3,
454    }
455}
456
457impl_numeric_string_enum! {
458    GenderPreferences {
459        Men = 0,
460        Women = 1,
461        Everyone = 2,
462    }
463}
464
465impl_profile_preference_enum! {
466    EthnicityValue,
467    EthnicityProfile,
468    EthnicityPreference {
469        AmericanIndian = 1,
470        BlackAfrican = 2,
471        EastAsian = 3,
472        Hispanic = 4,
473        MiddleEastern = 5,
474        PacificIslander = 6,
475        SouthAsian = 7,
476        White = 8,
477        Other = 9,
478    }
479}
480
481impl_profile_preference_enum! {
482    ReligionValue,
483    ReligionProfile,
484    ReligionPreference {
485        Spiritual = 1,
486        Catholic = 2,
487        Christian = 3,
488        Hindu = 4,
489        Jewish = 5,
490        Muslim = 6,
491        Buddhist = 7,
492        Agnostic = 8,
493        Atheist = 9,
494        Other = 10,
495        Sikh = 11,
496    }
497}
498
499impl_profile_preference_enum! {
500    PoliticsValue,
501    PoliticsProfile,
502    PoliticsPreference {
503        Liberal = 1,
504        Moderate = 2,
505        Conservative = 3,
506        NotPolitical = 4,
507        Other = 5,
508    }
509}
510
511impl_profile_preference_enum! {
512    EducationAttainedValue,
513    EducationAttainedProfile,
514    EducationAttainedPreference {
515        HighSchool = 1,
516        TradeSchool = 2,
517        InCollege = 3,
518        Undergraduate = 4,
519        InGradSchool = 5,
520        Graduate = 6,
521    }
522}
523
524impl_profile_preference_enum! {
525    RelationshipTypeValue,
526    RelationshipTypeProfile,
527    RelationshipTypePreference {
528        Monogamy = 1,
529        EthicalNonMonogamy = 2,
530        FiguringOutTheirRelationshipType = 3,
531    }
532}
533
534// QuestionId is actually a String UUID in the API, not an enum
535pub type QuestionId = String;
536
537#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
538#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
539#[serde(rename_all = "UPPERCASE")]
540pub enum LanguageEnum {
541    En,
542    Es,
543    Fr,
544    De,
545    It,
546    Pt,
547    Ru,
548    Zh,
549    Ja,
550    Ko,
551    Ar,
552    Hi,
553    Other,
554}