use crate::{ Result, AdapterPool, db::{ UserId, Feed, feed::UnparsedFeed, }, }; pub struct UnparsedUser { pub id: i64, pub name: String, } impl UnparsedUser { pub fn parse(self) -> Result { Ok(User { id: UserId(self.id), name: self.name }) } } pub struct User { id: UserId, name: String, } impl User { pub fn id(&self) -> UserId { self.id } pub fn name(&self) -> &str { &self.name } pub async fn get(pool: &AdapterPool, id: UserId) -> Result { let user = sqlx::query_as!( UnparsedUser, "SELECT id, name FROM users WHERE id = ?", id.0 ).fetch_one(&pool.0).await?.parse(); user } pub async fn get_all(pool: &AdapterPool) -> Result> { let users: Result> = sqlx::query_as!( UnparsedUser, "SELECT id, name FROM users" ).fetch_all(&pool.0).await?.into_iter().map(UnparsedUser::parse).collect(); users } pub async fn create(pool: &AdapterPool, name: &str) -> Result { let result = sqlx::query!( "INSERT INTO users (name) VALUES (?) RETURNING id, name", name ).fetch_one(&pool.0).await?; Ok(Self { id: UserId(result.id), name: result.name, }) } pub async fn update_name( pool: &AdapterPool, id: UserId, new_name: &str ) -> Result<()> { sqlx::query!( "UPDATE users SET name = ? WHERE id = ?", new_name, id.0 ).execute(&pool.0).await?; Ok(()) } pub async fn get_feeds(&self, pool: &AdapterPool) -> Result> { let feeds: Result> = sqlx::query_as!( UnparsedFeed, "SELECT id, title FROM feeds WHERE user_id = ?", self.id.0 ).fetch_all(&pool.0).await?.into_iter() .map(UnparsedFeed::parse).collect(); feeds } } #[cfg(test)] mod tests { use super::*; use crate::{ db::Feed, test_utils::{ USERNAME, USERNAME2, FEED_TITLE, FEED_TITLE2, setup_adapter, }, }; #[test] fn parse() { const UID: i64 = 1; let unparsed_user = UnparsedUser { id: UID, name: USERNAME.to_string(), }; let user = unparsed_user.parse().unwrap(); assert_eq!(user.id.0, UID); assert_eq!(user.name, USERNAME); } #[tokio::test] async fn get() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let new_user = User::create(pool, USERNAME).await.unwrap(); let fetched_user = User::get(pool, new_user.id).await.unwrap(); assert_eq!(fetched_user.name, USERNAME); assert_eq!(fetched_user.id.0, 1); } #[tokio::test] async fn get_all() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); User::create(pool, USERNAME).await.unwrap(); User::create(pool, USERNAME2).await.unwrap(); let users = User::get_all(pool).await.unwrap(); assert_eq!(users.len(), 2); assert!(users.iter().any(|u| u.name == USERNAME)); assert!(users.iter().any(|u| u.name == USERNAME2)); } #[tokio::test] async fn create_user() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let user = User::create(pool, USERNAME).await.unwrap(); assert_eq!(user.name, USERNAME); assert_eq!(user.id.0, 1); } #[tokio::test] async fn create_duplicate_user() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); User::create(pool, USERNAME).await.unwrap(); let duplicate_user = User::create(pool, USERNAME).await; assert!(duplicate_user.is_err()); } #[tokio::test] async fn update_name() { const NEW_USERNAME: &str = "Alicia"; assert!(NEW_USERNAME != USERNAME); let adapter = setup_adapter().await; let pool = adapter.get_pool(); let user = User::create(pool, USERNAME).await.unwrap(); User::update_name(pool, user.id, NEW_USERNAME).await.unwrap(); let updated = User::get(pool, user.id).await.unwrap(); assert_eq!(updated.name, NEW_USERNAME); } #[tokio::test] async fn update_name_to_duplicate() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let user1 = User::create(pool, USERNAME).await.unwrap(); User::create(pool, USERNAME2).await.unwrap(); let status = User::update_name(pool, user1.id, USERNAME2).await; assert!(status.is_err()); } #[tokio::test] async fn get_feeds() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let user = User::create(pool, USERNAME).await.unwrap(); Feed::create(pool, user.id, FEED_TITLE).await.unwrap(); Feed::create(pool, user.id, FEED_TITLE2).await.unwrap(); let feeds = user.get_feeds(pool).await.unwrap(); assert_eq!(feeds.len(), 2); assert!(feeds.iter().any(|f| f.title() == FEED_TITLE)); assert!(feeds.iter().any(|f| f.title() == FEED_TITLE2)); } #[tokio::test] async fn get_feeds_empty() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let user = User::create(pool, USERNAME).await.unwrap(); let feeds = user.get_feeds(pool).await.unwrap(); assert_eq!(feeds.len(), 0); } }