hypercall_db_diesel/
usernames.rs1use anyhow::Result;
7use async_trait::async_trait;
8use chrono::{DateTime, Utc};
9use diesel::prelude::*;
10use diesel_async::{AsyncConnection, RunQueryDsl};
11
12use crate::diesel_db::DieselDb;
13use crate::schema::usernames;
14use hypercall_db::{UsernameReader, UsernameRecord, UsernameWriteError, UsernameWriter};
15
16diesel::define_sql_function! {
19 fn lower(x: diesel::sql_types::Text) -> diesel::sql_types::Text;
21}
22
23#[derive(Debug, Clone, Queryable, Selectable, Insertable, AsChangeset)]
25#[diesel(table_name = usernames)]
26#[diesel(check_for_backend(diesel::pg::Pg))]
27struct UsernameRow {
28 pub wallet_address: String,
29 pub username: String,
30 pub created_at: DateTime<Utc>,
31 pub updated_at: DateTime<Utc>,
32}
33
34impl From<UsernameRow> for UsernameRecord {
35 fn from(row: UsernameRow) -> Self {
36 Self {
37 wallet_address: row.wallet_address,
38 username: row.username,
39 created_at: row.created_at,
40 updated_at: row.updated_at,
41 }
42 }
43}
44
45#[async_trait]
50impl UsernameReader for DieselDb {
51 async fn get_username_by_wallet(&self, wallet: &str) -> Result<Option<UsernameRecord>> {
52 let mut conn = self.get_conn().await?;
53 let wallet_lower = wallet.to_lowercase();
54
55 let row = usernames::table
56 .filter(lower(usernames::wallet_address).eq(&wallet_lower))
57 .first::<UsernameRow>(&mut conn)
58 .await
59 .optional()?;
60
61 Ok(row.map(Into::into))
62 }
63
64 async fn get_username_by_name(&self, username: &str) -> Result<Option<UsernameRecord>> {
65 let mut conn = self.get_conn().await?;
66 let name_lower = username.to_lowercase();
67
68 let row = usernames::table
69 .filter(lower(usernames::username).eq(&name_lower))
70 .first::<UsernameRow>(&mut conn)
71 .await
72 .optional()?;
73
74 Ok(row.map(Into::into))
75 }
76}
77
78#[async_trait]
83impl UsernameWriter for DieselDb {
84 async fn set_username(
85 &self,
86 wallet: &str,
87 username: &str,
88 now: DateTime<Utc>,
89 ) -> std::result::Result<(UsernameRecord, Option<String>), UsernameWriteError> {
90 let mut conn = self
91 .get_conn()
92 .await
93 .map_err(UsernameWriteError::Internal)?;
94 let wallet_lower = wallet.to_lowercase();
95 let wallet_owned = wallet.to_string();
96 let username_owned = username.to_string();
97
98 let (row, old_username) = conn
99 .transaction::<(UsernameRow, Option<String>), diesel::result::Error, _>(async |conn| {
100 let prev = usernames::table
102 .filter(lower(usernames::wallet_address).eq(&wallet_lower))
103 .select(usernames::username)
104 .first::<String>(&mut *conn)
105 .await
106 .optional()?;
107
108 diesel::delete(
110 usernames::table.filter(lower(usernames::wallet_address).eq(&wallet_lower)),
111 )
112 .execute(&mut *conn)
113 .await?;
114
115 let new_row = UsernameRow {
117 wallet_address: wallet_owned.clone(),
118 username: username_owned.clone(),
119 created_at: now,
120 updated_at: now,
121 };
122
123 let inserted = diesel::insert_into(usernames::table)
124 .values(&new_row)
125 .get_result::<UsernameRow>(&mut *conn)
126 .await?;
127
128 Ok((inserted, prev))
129 })
130 .await
131 .map_err(|e| {
132 if let diesel::result::Error::DatabaseError(
133 diesel::result::DatabaseErrorKind::UniqueViolation,
134 _,
135 ) = &e
136 {
137 return UsernameWriteError::UniqueViolation;
138 }
139 UsernameWriteError::Internal(
140 anyhow::Error::from(e).context("Failed to upsert username"),
141 )
142 })?;
143
144 Ok((row.into(), old_username))
145 }
146
147 async fn delete_username(&self, wallet: &str) -> Result<Option<UsernameRecord>> {
148 let mut conn = self.get_conn().await?;
149 let wallet_lower = wallet.to_lowercase();
150
151 let deleted_row: Option<UsernameRow> = conn
152 .transaction::<Option<UsernameRow>, diesel::result::Error, _>(async |conn| {
153 let existing = usernames::table
154 .filter(lower(usernames::wallet_address).eq(&wallet_lower))
155 .first::<UsernameRow>(&mut *conn)
156 .await
157 .optional()?;
158
159 if existing.is_some() {
160 diesel::delete(
161 usernames::table.filter(lower(usernames::wallet_address).eq(&wallet_lower)),
162 )
163 .execute(&mut *conn)
164 .await?;
165 }
166
167 Ok(existing)
168 })
169 .await?;
170
171 Ok(deleted_row.map(Into::into))
172 }
173}