summaryrefslogtreecommitdiff
path: root/src/users.rs
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2025-07-14 19:34:59 +0300
committerDawid Rycerz <dawid@rycerz.xyz>2025-07-14 19:34:59 +0300
commit50ce8cb96b2b218751c2fc2a6b19372f51846acc (patch)
treee2c634d2ce856062d527667d47815a05a53361c8 /src/users.rs
parent0ab2e5ba2b0631b28b5b1405559237b3913c878f (diff)
feat: rewrite in rust
Diffstat (limited to 'src/users.rs')
-rw-r--r--src/users.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/users.rs b/src/users.rs
new file mode 100644
index 0000000..baca6dd
--- /dev/null
+++ b/src/users.rs
@@ -0,0 +1,139 @@
+use serde::{Deserialize, Serialize};
+use sqlx::FromRow;
+use uuid::Uuid;
+
+#[derive(Debug, Serialize, Deserialize, FromRow, Clone, PartialEq, Eq)]
+pub struct User {
+ pub id: i64,
+ pub user_id: String, // API token
+ pub role: UserRole,
+}
+
+#[derive(Debug, Serialize, Deserialize, sqlx::Type, Clone, PartialEq, Eq)]
+#[sqlx(type_name = "TEXT")]
+pub enum UserRole {
+ #[serde(rename = "user")]
+ User,
+ #[serde(rename = "admin")]
+ Admin,
+}
+
+impl Default for UserRole {
+ fn default() -> Self {
+ UserRole::User
+ }
+}
+
+pub struct UserRepository<'a> {
+ pub db: &'a sqlx::SqlitePool,
+}
+
+impl<'a> UserRepository<'a> {
+ pub async fn list_users(&self) -> Result<Vec<User>, sqlx::Error> {
+ sqlx::query_as::<_, User>("SELECT id, user_id, role FROM users")
+ .fetch_all(self.db)
+ .await
+ }
+
+ pub async fn get_user_by_id(&self, id: i64) -> Result<Option<User>, sqlx::Error> {
+ sqlx::query_as::<_, User>("SELECT id, user_id, role FROM users WHERE id = ?")
+ .bind(id)
+ .fetch_optional(self.db)
+ .await
+ }
+
+ pub async fn get_user_by_user_id(&self, user_id: &str) -> Result<Option<User>, sqlx::Error> {
+ sqlx::query_as::<_, User>("SELECT id, user_id, role FROM users WHERE user_id = ?")
+ .bind(user_id)
+ .fetch_optional(self.db)
+ .await
+ }
+
+ pub async fn create_user(&self, user_id: Option<String>, role: Option<UserRole>) -> Result<User, sqlx::Error> {
+ let user_id = user_id.unwrap_or_else(|| Uuid::new_v4().to_string());
+ let role = role.unwrap_or_default();
+ sqlx::query_as::<_, User>(
+ "INSERT INTO users (user_id, role) VALUES (?, ?) RETURNING id, user_id, role"
+ )
+ .bind(user_id)
+ .bind(role)
+ .fetch_one(self.db)
+ .await
+ }
+
+ pub async fn update_user(&self, id: i64, role: UserRole) -> Result<User, sqlx::Error> {
+ sqlx::query_as::<_, User>(
+ "UPDATE users SET role = ? WHERE id = ? RETURNING id, user_id, role"
+ )
+ .bind(role)
+ .bind(id)
+ .fetch_one(self.db)
+ .await
+ }
+
+ pub async fn delete_user(&self, id: i64) -> Result<(), sqlx::Error> {
+ sqlx::query("DELETE FROM users WHERE id = ?")
+ .bind(id)
+ .execute(self.db)
+ .await?;
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use sqlx::{SqlitePool, Executor};
+ use tokio;
+
+ async fn setup_db() -> SqlitePool {
+ let pool = SqlitePool::connect(":memory:").await.unwrap();
+ pool.execute(
+ "CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id TEXT NOT NULL UNIQUE,
+ role TEXT NOT NULL DEFAULT 'user'
+ );"
+ ).await.unwrap();
+ pool
+ }
+
+ #[tokio::test]
+ async fn test_create_and_get_user() {
+ let db = setup_db().await;
+ let repo = UserRepository { db: &db };
+ let user = repo.create_user(None, Some(UserRole::Admin)).await.unwrap();
+ assert_eq!(user.role, UserRole::Admin);
+ let fetched = repo.get_user_by_user_id(&user.user_id).await.unwrap().unwrap();
+ assert_eq!(fetched.user_id, user.user_id);
+ }
+
+ #[tokio::test]
+ async fn test_update_user() {
+ let db = setup_db().await;
+ let repo = UserRepository { db: &db };
+ let user = repo.create_user(None, Some(UserRole::User)).await.unwrap();
+ let updated = repo.update_user(user.id, UserRole::Admin).await.unwrap();
+ assert_eq!(updated.role, UserRole::Admin);
+ }
+
+ #[tokio::test]
+ async fn test_delete_user() {
+ let db = setup_db().await;
+ let repo = UserRepository { db: &db };
+ let user = repo.create_user(None, None).await.unwrap();
+ repo.delete_user(user.id).await.unwrap();
+ let fetched = repo.get_user_by_id(user.id).await.unwrap();
+ assert!(fetched.is_none());
+ }
+
+ #[tokio::test]
+ async fn test_list_users() {
+ let db = setup_db().await;
+ let repo = UserRepository { db: &db };
+ repo.create_user(None, Some(UserRole::User)).await.unwrap();
+ repo.create_user(None, Some(UserRole::Admin)).await.unwrap();
+ let users = repo.list_users().await.unwrap();
+ assert_eq!(users.len(), 2);
+ }
+} \ No newline at end of file