{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
module Imm.Database.FeedTable where
import Imm.Aeson
import Imm.Database as Database
import Imm.Logger as Logger
import Imm.Prelude
import Imm.Pretty
import Control.Monad.Time
import Data.Aeson
import Data.Set (Set)
import Data.Time
import URI.ByteString
newtype FeedID = FeedID URI
deriving(Eq, Ord, Show)
prettyFeedID :: FeedID -> Doc AnsiStyle
prettyFeedID (FeedID uri) = prettyURI uri
instance FromJSON FeedID where
parseJSON = fmap FeedID . parseJsonURI
instance ToJSON FeedID where
toJSON (FeedID uri) = toJsonURI uri
instance Pretty FeedID where
pretty (FeedID uri) = prettyURI uri
data DatabaseEntry = DatabaseEntry
{ entryURI :: URI
, entryTags :: Set Text
, entryReadHashes :: Set Int
, entryLastCheck :: Maybe UTCTime
} deriving(Eq, Show)
prettyDatabaseEntry :: DatabaseEntry -> Doc AnsiStyle
prettyDatabaseEntry entry = magenta feedID
<++> indent 3 tags
<++> indent 3 ("Last checked:" <+> lastCheck)
where feedID = prettyURI $ entryURI entry
tags = sep $ map ((<>) "#" . pretty) $ toList $ entryTags entry
lastCheck = format $ entryLastCheck entry
format = maybe "never" (fromString . formatTime defaultTimeLocale "%F %R")
instance FromJSON DatabaseEntry where
parseJSON (Object v) = DatabaseEntry <$> (parseJsonURI =<< v .: "uri") <*> v .: "tags" <*> v.: "readHashes" <*> v .: "lastCheck"
parseJSON _ = mzero
instance ToJSON DatabaseEntry where
toJSON entry = object
[ "uri" .= toJsonURI (entryURI entry)
, "tags" .= entryTags entry
, "readHashes" .= entryReadHashes entry
, "lastCheck" .= entryLastCheck entry
]
newDatabaseEntry :: FeedID -> Set Text -> DatabaseEntry
newDatabaseEntry (FeedID uri) tags = DatabaseEntry uri tags mempty Nothing
data FeedTable = FeedTable
deriving(Show)
instance Pretty FeedTable where
pretty _ = "Feeds table"
instance Table FeedTable where
type Key FeedTable = FeedID
type Entry FeedTable = DatabaseEntry
rep = FeedTable
data FeedStatus = Unknown | New | LastUpdate UTCTime
instance Pretty FeedStatus where
pretty Unknown = "Unknown"
pretty New = "New"
pretty (LastUpdate x) = "Last update:" <+> pretty (formatTime defaultTimeLocale rfc822DateFormat x)
newtype Database = Database [DatabaseEntry]
deriving (Eq, Show)
register :: MonadThrow m => Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> Set Text -> m ()
register logger database feedID tags = do
log logger Info $ "Registering feed" <+> magenta (pretty feedID) <> "..."
insert logger database feedID $ newDatabaseEntry feedID tags
getStatus :: MonadCatch m => Database.Handle m FeedTable -> FeedID -> m FeedStatus
getStatus database feedID = handleAny (\_ -> return Unknown) $ do
result <- fmap Just (fetch database feedID) `catchAny` (\_ -> return Nothing)
return $ maybe New LastUpdate $ entryLastCheck =<< result
addReadHash :: MonadThrow m => Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> Int -> m ()
addReadHash logger database feedID hash = do
log logger Debug $ "Adding read hash:" <+> pretty hash <> "..."
update database feedID f
where f a = a { entryReadHashes = insertSet hash $ entryReadHashes a }
markAsRead :: (MonadTime m, MonadThrow m)
=> Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> m ()
markAsRead logger database feedID = do
log logger Debug $ "Marking feed as read:" <+> pretty feedID <> "..."
utcTime <- currentTime
update database feedID (f utcTime)
where f time a = a { entryLastCheck = Just time }
markAsUnread :: MonadThrow m => Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> m ()
markAsUnread logger database feedID = do
log logger Info $ "Marking feed as unread:" <+> prettyFeedID feedID <> "..."
update database feedID $ \a -> a { entryReadHashes = mempty, entryLastCheck = Nothing }