2026-01-20 16:08:36 -08:00
|
|
|
use crate::{
|
|
|
|
|
Result,
|
|
|
|
|
Item,
|
|
|
|
|
Channel,
|
2026-01-20 16:36:14 -08:00
|
|
|
channel::{
|
|
|
|
|
UnparsedChannel,
|
|
|
|
|
ChannelId,
|
|
|
|
|
},
|
|
|
|
|
user::UserId,
|
2026-01-20 16:08:36 -08:00
|
|
|
};
|
|
|
|
|
use sqlx::SqlitePool;
|
|
|
|
|
|
2026-01-21 12:47:47 -08:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
|
pub struct FeedId(i64);
|
2026-01-20 16:08:36 -08:00
|
|
|
impl From<FeedId> for i64 { fn from(id: FeedId) -> Self { id.0 } }
|
|
|
|
|
|
2026-01-20 16:36:14 -08:00
|
|
|
pub struct UnparsedFeed {
|
|
|
|
|
pub id: i64,
|
|
|
|
|
pub title: String,
|
|
|
|
|
}
|
|
|
|
|
impl UnparsedFeed {
|
|
|
|
|
pub fn parse(self) -> Result<Feed> {
|
|
|
|
|
Ok(Feed {
|
|
|
|
|
id: FeedId(self.id),
|
|
|
|
|
title: self.title,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-20 16:08:36 -08:00
|
|
|
pub struct Feed {
|
2026-01-21 12:47:47 -08:00
|
|
|
id: FeedId,
|
|
|
|
|
title: String,
|
2026-01-20 16:08:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Feed {
|
2026-01-21 12:47:47 -08:00
|
|
|
pub fn id(&self) -> FeedId { self.id }
|
|
|
|
|
pub fn title(&self) -> &str { &self.title }
|
|
|
|
|
|
2026-01-20 16:36:14 -08:00
|
|
|
pub async fn create(
|
|
|
|
|
pool: &SqlitePool, id: UserId, name: &str
|
|
|
|
|
) -> Result<Self> {
|
2026-01-21 12:47:47 -08:00
|
|
|
let int_id = i64::from(id);
|
2026-01-20 16:36:14 -08:00
|
|
|
let new_feed = sqlx::query_as!(
|
|
|
|
|
UnparsedFeed,
|
|
|
|
|
"INSERT INTO feeds (user_id, title)
|
|
|
|
|
VALUES (?, ?)
|
2026-01-21 12:47:47 -08:00
|
|
|
RETURNING id as `id!`, title",
|
|
|
|
|
int_id, name
|
2026-01-20 16:36:14 -08:00
|
|
|
).fetch_one(pool).await?.parse();
|
|
|
|
|
|
|
|
|
|
new_feed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn add_channel(
|
|
|
|
|
pool: &SqlitePool, id: FeedId, channel_id: ChannelId
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
sqlx::query!(
|
|
|
|
|
"INSERT INTO feed_channels (feed_id, channel_id)
|
|
|
|
|
VALUES (?, ?)",
|
|
|
|
|
id.0, channel_id.0
|
|
|
|
|
).execute(pool).await?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-20 16:08:36 -08:00
|
|
|
pub async fn get_items(
|
|
|
|
|
pool: &SqlitePool, id: FeedId, limit: u8, offset: u32
|
|
|
|
|
) -> Result<Vec<Item>> {
|
|
|
|
|
let items = sqlx::query_as!(
|
|
|
|
|
Item,
|
2026-01-20 16:36:14 -08:00
|
|
|
"SELECT item_id as id FROM feed_items
|
2026-01-20 16:08:36 -08:00
|
|
|
WHERE feed_id = ? AND archived = FALSE
|
|
|
|
|
ORDER BY score DESC
|
|
|
|
|
LIMIT ? OFFSET ?",
|
|
|
|
|
id.0, limit, offset
|
|
|
|
|
).fetch_all(pool).await?;
|
|
|
|
|
|
|
|
|
|
Ok(items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_channels(
|
|
|
|
|
pool: &SqlitePool, id: FeedId
|
|
|
|
|
) -> Result<Vec<Channel>> {
|
|
|
|
|
let channels: Result<Vec<Channel>> = 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 = ?",
|
|
|
|
|
id.0
|
|
|
|
|
).fetch_all(pool).await?.into_iter()
|
|
|
|
|
.map(UnparsedChannel::parse).collect();
|
|
|
|
|
|
|
|
|
|
channels
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|