polyester/codecs/decode/
auth.rs1use crate::codecs::scalars::{format_id, format_uint64_id};
4use crate::models::{
5 AccountIdentity, MeResult, UserProfile, UsernameHistoryEntry, UsernameHistoryList,
6};
7use crate::proto::auth::v1::{
8 AccountIdentity as ProtoAccountIdentity, GetUsernameHistoryResponse, MeResponse,
9 UserProfile as ProtoUserProfile,
10};
11
12fn timestamp_ms(ts: Option<&buffa_types::google::protobuf::Timestamp>) -> Option<i64> {
13 ts.map(|t| t.seconds.saturating_mul(1000) + (t.nanos as i64) / 1_000_000)
14}
15
16pub fn me_from_proto(msg: &MeResponse) -> MeResult {
17 MeResult {
18 account_id: format_uint64_id(msg.account_id),
19 api_key_id: msg.api_key_id.map(format_uint64_id),
20 username: if msg.username.is_empty() {
21 None
22 } else {
23 Some(msg.username.clone())
24 },
25 root_smart_account_address: if msg.root_smart_account_address.is_empty() {
26 None
27 } else {
28 Some(msg.root_smart_account_address.clone())
29 },
30 }
31}
32
33pub fn profile_from_proto(msg: &ProtoUserProfile) -> UserProfile {
34 UserProfile {
35 username: msg.username.clone(),
36 bio: msg.bio.clone(),
37 website: msg.website.clone(),
38 twitter: msg.twitter.clone(),
39 twitter_verified: msg.twitter_verified,
40 discord: msg.discord.clone(),
41 discord_verified: msg.discord_verified,
42 avatar_url: msg.avatar_url.clone(),
43 created_at_ms: timestamp_ms(msg.created_at.as_option()),
44 next_username_change_at_ms: timestamp_ms(msg.next_username_change_at.as_option()),
45 vip_tier: msg.vip_tier,
46 username_unlocked: msg.username_unlocked,
47 }
48}
49
50pub fn username_history_from_proto(msg: &GetUsernameHistoryResponse) -> UsernameHistoryList {
51 UsernameHistoryList {
52 entries: msg
53 .history
54 .iter()
55 .map(|e| UsernameHistoryEntry {
56 username: e.username.clone(),
57 changed_at_ms: timestamp_ms(e.set_at.as_option()),
58 })
59 .collect(),
60 }
61}
62
63pub fn account_identity_from_proto(msg: &ProtoAccountIdentity) -> AccountIdentity {
64 AccountIdentity {
65 account_id: format_id(msg.account_id),
66 username: msg.username.clone(),
67 avatar_url: msg.avatar_url.clone(),
68 root_smart_account_address: msg.root_smart_account_address.clone(),
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::codecs::scalars::format_uint64_id;
76 use crate::proto::auth::v1::MeResponse;
77
78 #[test]
79 fn me_from_proto_formats_ids() {
80 let msg = MeResponse {
81 account_id: 42,
82 api_key_id: Some(99),
83 username: "alice".into(),
84 root_smart_account_address: "0xabc".into(),
85 ..Default::default()
86 };
87 let me = me_from_proto(&msg);
88 assert_eq!(me.account_id, format_uint64_id(42));
89 assert_eq!(
90 me.api_key_id.as_deref(),
91 Some(format_uint64_id(99).as_str())
92 );
93 assert_eq!(me.username.as_deref(), Some("alice"));
94 assert_eq!(me.root_smart_account_address.as_deref(), Some("0xabc"));
95 }
96
97 #[test]
98 fn profile_from_proto_maps_fields() {
99 let msg = ProtoUserProfile {
100 username: "alice".into(),
101 bio: "hi".into(),
102 twitter_verified: true,
103 vip_tier: 2,
104 username_unlocked: true,
105 ..Default::default()
106 };
107 let profile = profile_from_proto(&msg);
108 assert_eq!(profile.username, "alice");
109 assert_eq!(profile.bio, "hi");
110 assert!(profile.twitter_verified);
111 assert_eq!(profile.vip_tier, 2);
112 assert!(profile.username_unlocked);
113 }
114}