use anyhow::Result; use feedsignal_db::Db; use uuid::Uuid; /// Subscribes to a feed by URL: registers it (or updates its title if /// already subscribed) and does an immediate first fetch so the reader /// isn't empty until the next scheduled poll. Returns the number of /// articles pulled in on this fetch. /// /// Fetches under a throwaway id before writing anything, so there's exactly /// one write to the `feeds` table (`upsert_feed` is the single source of /// truth for the real id — the existing one on a re-subscribe, a fresh one /// otherwise) and no half-registered row left behind if the fetch fails /// (e.g. the URL is well-formed but points at nothing, or the feed no /// longer exists — `fetch_feed` surfaces both as an `Err` via /// `error_for_status`/feed-rs parse failure, which becomes the error /// message shown in the subscribe form). pub async fn subscribe(db: Db, url: String) -> Result { anyhow::ensure!(!url.trim().is_empty(), "feed URL is required"); let url = url.trim(); let (title, mut articles) = feedsignal_feeds::fetch_feed(url, Uuid::new_v4()).await?; let feed_id = db.upsert_feed(url, &title).await?; for article in &mut articles { article.feed_id = feed_id; } let count = articles.len(); for article in &articles { db.insert_article(article).await?; } Ok(count) }