1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct ConnectUser {
7 pub id: String,
9
10 pub name: String,
12
13 pub email: Option<String>,
15
16 pub email_verified: Option<bool>,
18
19 pub avatar_url: Option<String>,
21
22 pub raw_data: Value,
25
26 #[serde(with = "secret_serde")]
28 pub access_token: secrecy::SecretString,
29
30 #[serde(with = "opt_secret_serde")]
32 pub refresh_token: Option<secrecy::SecretString>,
33
34 pub expires_in: Option<u64>,
36}
37
38mod secret_serde {
39 use secrecy::{ExposeSecret, SecretString};
40 use serde::{Deserialize, Deserializer, Serialize, Serializer};
41
42 pub fn serialize<S>(secret: &SecretString, serializer: S) -> Result<S::Ok, S::Error>
43 where
44 S: Serializer,
45 {
46 secret.expose_secret().serialize(serializer)
47 }
48
49 pub fn deserialize<'de, D>(deserializer: D) -> Result<SecretString, D::Error>
50 where
51 D: Deserializer<'de>,
52 {
53 let s = String::deserialize(deserializer)?;
54 Ok(SecretString::from(s))
55 }
56}
57
58mod opt_secret_serde {
59 use secrecy::{ExposeSecret, SecretString};
60 use serde::{Deserialize, Deserializer, Serialize, Serializer};
61
62 pub fn serialize<S>(secret: &Option<SecretString>, serializer: S) -> Result<S::Ok, S::Error>
63 where
64 S: Serializer,
65 {
66 match secret {
67 Some(s) => s.expose_secret().serialize(serializer),
68 None => serializer.serialize_none(),
69 }
70 }
71
72 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<SecretString>, D::Error>
73 where
74 D: Deserializer<'de>,
75 {
76 let opt = Option::<String>::deserialize(deserializer)?;
77 Ok(opt.map(SecretString::from))
78 }
79}
80
81use async_trait::async_trait;
82
83#[async_trait]
87pub trait IntoDatabaseUser<T> {
88 async fn sync_from_oauth(profile: &ConnectUser) -> Result<T, crate::error::ConnectError>;
91}
92
93#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
95pub struct DeviceAuthorizationResponse {
96 pub device_code: String,
97 pub user_code: String,
98 pub verification_uri: String,
99 pub verification_uri_complete: Option<String>,
100 pub expires_in: u64,
101 pub interval: Option<u64>,
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use serde_json::json;
108
109 #[test]
110 fn test_connect_user_serialization() {
111 let user = ConnectUser {
112 id: "123".to_string(),
113 name: "Test User".to_string(),
114 email: Some("test@example.com".to_string()),
115 email_verified: Some(true),
116 avatar_url: Some("https://example.com/avatar.png".to_string()),
117 raw_data: json!({"custom_field": "custom_value"}),
118 access_token: secrecy::SecretString::from("access123".to_string()),
119 refresh_token: Some(secrecy::SecretString::from("refresh123".to_string())),
120 expires_in: Some(3600),
121 };
122
123 let serialized = serde_json::to_string(&user).unwrap();
124 let deserialized: ConnectUser = serde_json::from_str(&serialized).unwrap();
125
126 assert_eq!(user.id, deserialized.id);
127 assert_eq!(user.name, deserialized.name);
128 assert_eq!(user.email, deserialized.email);
129 assert_eq!(user.email_verified, deserialized.email_verified);
130 assert_eq!(user.avatar_url, deserialized.avatar_url);
131 assert_eq!(user.raw_data, deserialized.raw_data);
132 use secrecy::ExposeSecret;
133 assert_eq!(
134 user.access_token.expose_secret(),
135 deserialized.access_token.expose_secret()
136 );
137 assert_eq!(
138 user.refresh_token.as_ref().map(|s| s.expose_secret()),
139 deserialized
140 .refresh_token
141 .as_ref()
142 .map(|s| s.expose_secret())
143 );
144 assert_eq!(user.expires_in, deserialized.expires_in);
145 }
146}