packages feed

scrapbook-0.3.2: src/ScrapBook/Feed.hs

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedLabels  #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds         #-}
{-# LANGUAGE TypeFamilies      #-}
{-# LANGUAGE TypeOperators     #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module ScrapBook.Feed
  ( writeFeed
  ) where

import           RIO
import           RIO.FilePath
import qualified RIO.Text                 as T
import qualified RIO.Text.Lazy            as T.Lazy

import           Control.Monad.IO.Class   (liftIO)
import           Data.Default             (def)
import           Data.Extensible
import           ScrapBook.Collecter
import           ScrapBook.Data.Config
import           ScrapBook.Data.Site
import           ScrapBook.Feed.Atom
import           ScrapBook.Feed.RSS       (fromRSSFeed)
import           ScrapBook.Fetch.Internal (Fetch (..), fetchHtml,
                                           throwFetchError)
import           ScrapBook.Write.Internal (Write (..), throwWriteError)
import           Text.Feed.Import         (parseFeedString)
import           Text.Feed.Types          (Feed (..))
import qualified Text.XML                 as XML

instance Fetch ("feed" >: Text) where
  fetchFrom _ site url = do
    resp <- T.unpack <$> fetchHtml url
    case parseFeedString resp of
      Just (AtomFeed feed) -> pure $ fromAtomFeed site (toAtomConfig url) feed
      Just (RSSFeed feed)  -> pure $ fromRSSFeed  site feed
      _                    -> throwFetchError (Right "can't parse feed.")

instance Write ("feed" >: ()) where
  writeTo _ conf posts = do
    conf' <-
      maybe (throwWriteError "add feed config on yaml.") pure $ conf ^. #feed
    case toDocument (toAtomFeed conf' posts) of
      Left err   -> throwWriteError $ mconcat (toList err)
      Right docs -> pure $ T.Lazy.toStrict (XML.renderText def docs)
  fileName' _ conf = feedName $ fromMaybe mempty (conf ^. #feed)
  updateFileName' _ path conf =
    conf & #feed `over` fmap (over #name $ maybe (pure $ T.pack name) pure)
    where
      name = replaceExtension (takeFileName path) "xml"

writeFeed :: IsSiteFields xs =>
  FilePath -> FeedConfig -> [Post (Record xs)] -> Collecter ()
writeFeed dir conf posts =
  case toDocument (toAtomFeed conf posts) of
    Left err   -> throwWriteError $ mconcat (toList err)
    Right docs -> liftIO $ XML.writeFile def path docs
  where
    path = mconcat [dir, "/", feedName conf]