use crate::{ AdapterPool, Result, db::{ Channel, ChannelKey, FeedKey, Item, UserKey, channel::UnparsedChannel, item::UnparsedItem, }, }; pub struct UnparsedFeed { pub id: i64, pub title: String, } impl UnparsedFeed { pub fn parse(self) -> Result { Ok(Feed { key: FeedKey(self.id), title: self.title, }) } } pub struct Feed { key: FeedKey, title: String, } impl Feed { pub fn key(&self) -> FeedKey { self.key } pub fn title(&self) -> &str { &self.title } pub async fn get( pool: &AdapterPool, key: FeedKey ) -> Result { sqlx::query_as!( UnparsedFeed, "SELECT id, title FROM feeds WHERE id = ?", key.0 ).fetch_one(&pool.0).await?.parse() } pub async fn create( pool: &AdapterPool, user_key: UserKey, title: &str ) -> Result { sqlx::query_as!( UnparsedFeed, "INSERT INTO feeds (user_id, title) VALUES (?, ?) RETURNING id as `id!`, title", user_key.0, title ).fetch_one(&pool.0).await?.parse() } pub async fn update_title( pool: &AdapterPool, key: FeedKey, new_title: &str ) -> Result<()> { sqlx::query!( "UPDATE feeds SET title = ? WHERE id = ?", new_title, key.0 ).execute(&pool.0).await?; Ok(()) } pub async fn add_channel( &self, pool: &AdapterPool, channel_key: ChannelKey ) -> Result<()> { sqlx::query!( "INSERT INTO feed_channels (feed_id, channel_id) VALUES (?, ?)", self.key.0, channel_key.0 ).execute(&pool.0).await?; Ok(()) } pub async fn get_items( &self, pool: &AdapterPool, limit: u8, offset: u32 ) -> Result> { sqlx::query_as!( UnparsedItem, "SELECT i.id as `id!`, i.channel_id, i.fetched_at, i.title, i.description, i.content FROM items i JOIN feed_items fi on i.id = fi.item_id WHERE feed_id = ? AND archived = FALSE ORDER BY score DESC LIMIT ? OFFSET ?", self.key.0, limit, offset ).fetch_all(&pool.0).await?.into_iter().map(UnparsedItem::parse).collect() } pub async fn get_channels( &self, pool: &AdapterPool ) -> Result> { sqlx::query_as!( UnparsedChannel, "SELECT c.id as `id!`, c.title, c.link, c.description, c.last_fetched FROM channels c JOIN feed_channels fc on c.id = fc.channel_id WHERE fc.feed_id = ?", self.key.0 ).fetch_all(&pool.0).await?.into_iter() .map(UnparsedChannel::parse).collect() } } #[cfg(test)] mod tests { use super::*; use crate::{ db::User, test_utils::{ FEED_TITLE, USERNAME, FEED1, FEED2, setup_adapter, setup_feed, setup_channel, } }; use reqwest::Url; #[test] fn parse() { const FID: i64 = 1; let uf = UnparsedFeed { id: FID, title: FEED_TITLE.to_string(), }; let f = uf.parse().unwrap(); assert_eq!(f.key.0, FID); assert_eq!(f.title, FEED_TITLE); } #[tokio::test] async fn get() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let feed = setup_feed(pool).await; let gotten_feed = Feed::get(pool, feed.key).await.unwrap(); assert_eq!(feed.key, gotten_feed.key); assert_eq!(feed.title, gotten_feed.title); } #[tokio::test] async fn create() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let user = User::create(pool, USERNAME).await.unwrap(); let feed = Feed::create(pool, user.key(), FEED_TITLE).await.unwrap(); assert!(feed.key().0 > 0); assert_eq!(feed.title(), FEED_TITLE); } #[tokio::test] async fn update_title() { const NEW_FEED_TITLE: &str = "My NEW feed!"; let adapter = setup_adapter().await; let pool = adapter.get_pool(); let feed = setup_feed(pool).await; Feed::update_title(pool, feed.key(), NEW_FEED_TITLE).await.unwrap(); let updated = Feed::get(pool, feed.key()).await.unwrap(); assert_eq!(updated.title(), NEW_FEED_TITLE); } #[tokio::test] async fn add_channel() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let feed = setup_feed(pool).await; let channel = setup_channel(pool).await; feed.add_channel(pool, channel.key()).await.unwrap(); let channels = feed.get_channels(pool).await.unwrap(); let gotten_channel = &channels[0]; assert_eq!(gotten_channel.key().0, channel.key().0); } #[tokio::test] async fn get_channels() { let adapter = setup_adapter().await; let pool = adapter.get_pool(); let feed = setup_feed(pool).await; let url1 = Url::parse(FEED1).unwrap(); let channel1 = Channel::get_or_create(pool, url1).await.unwrap(); let url2 = Url::parse(FEED2).unwrap(); let channel2 = Channel::get_or_create(pool, url2).await.unwrap(); feed.add_channel(pool, channel1.key()).await.unwrap(); feed.add_channel(pool, channel2.key()).await.unwrap(); let channels = feed.get_channels(pool).await.unwrap(); assert_eq!(channels.len(), 2); } }