use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectUser {
pub id: String,
pub name: String,
pub email: Option<String>,
pub email_verified: Option<bool>,
pub avatar_url: Option<String>,
pub raw_data: Value,
#[serde(with = "secret_serde")]
pub access_token: secrecy::SecretString,
#[serde(with = "opt_secret_serde")]
pub refresh_token: Option<secrecy::SecretString>,
pub expires_in: Option<u64>,
}
mod secret_serde {
use secrecy::{ExposeSecret, SecretString};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(secret: &SecretString, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
secret.expose_secret().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<SecretString, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(SecretString::from(s))
}
}
mod opt_secret_serde {
use secrecy::{ExposeSecret, SecretString};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(secret: &Option<SecretString>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match secret {
Some(s) => s.expose_secret().serialize(serializer),
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<SecretString>, D::Error>
where
D: Deserializer<'de>,
{
let opt = Option::<String>::deserialize(deserializer)?;
Ok(opt.map(SecretString::from))
}
}
use async_trait::async_trait;
#[async_trait]
pub trait IntoDatabaseUser<T> {
async fn sync_from_oauth(profile: &ConnectUser) -> Result<T, crate::error::ConnectError>;
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DeviceAuthorizationResponse {
pub device_code: String,
pub user_code: String,
pub verification_uri: String,
pub verification_uri_complete: Option<String>,
pub expires_in: u64,
pub interval: Option<u64>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_connect_user_serialization() {
let user = ConnectUser {
id: "123".to_string(),
name: "Test User".to_string(),
email: Some("test@example.com".to_string()),
email_verified: Some(true),
avatar_url: Some("https://example.com/avatar.png".to_string()),
raw_data: json!({"custom_field": "custom_value"}),
access_token: secrecy::SecretString::from("access123".to_string()),
refresh_token: Some(secrecy::SecretString::from("refresh123".to_string())),
expires_in: Some(3600),
};
let serialized = serde_json::to_string(&user).unwrap();
let deserialized: ConnectUser = serde_json::from_str(&serialized).unwrap();
assert_eq!(user.id, deserialized.id);
assert_eq!(user.name, deserialized.name);
assert_eq!(user.email, deserialized.email);
assert_eq!(user.email_verified, deserialized.email_verified);
assert_eq!(user.avatar_url, deserialized.avatar_url);
assert_eq!(user.raw_data, deserialized.raw_data);
use secrecy::ExposeSecret;
assert_eq!(
user.access_token.expose_secret(),
deserialized.access_token.expose_secret()
);
assert_eq!(
user.refresh_token.as_ref().map(|s| s.expose_secret()),
deserialized
.refresh_token
.as_ref()
.map(|s| s.expose_secret())
);
assert_eq!(user.expires_in, deserialized.expires_in);
}
}