imm-2.1.2.0: src/main/Database/Record.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UnicodeSyntax #-}
module Database.Record (
FeedStatus (..),
markFeedAsUnprocessed,
touchFeed,
FeedItemStatus (..),
markItemAsProcessed,
markItemAsUnprocessed,
FeedRecord (..),
mkFeedRecord,
FeedItemRecord (..),
prettyItemRecord,
mkFeedItemRecord,
Inserted,
NotInserted,
)
where
import Control.Monad.Time
import Data.Aeson
import qualified Data.Text as Text (null)
import Data.Time
import Imm.Feed
import Imm.Pretty
import URI.ByteString
-- | Stateful information on a feed
data FeedStatus = FeedStatus
{ _feedTags ∷ Set Text
, _feedLastUpdate ∷ Maybe UTCTime
}
deriving (Eq, Generic, Ord, Read, Show, Typeable)
feedStatusOptions ∷ Options
feedStatusOptions =
defaultOptions
{ fieldLabelModifier = camelTo2 '_' . drop (length @[] "_feed")
, omitNothingFields = True
}
instance ToJSON FeedStatus where
toJSON = genericToJSON feedStatusOptions
toEncoding = genericToEncoding feedStatusOptions
instance FromJSON FeedStatus where
parseJSON = genericParseJSON feedStatusOptions
instance Pretty FeedStatus where
pretty FeedStatus{..} = tags <++> "Last update:" <+> lastUpdate
where
tags = sep $ map ((<>) "#" . pretty) $ toList _feedTags
lastUpdate = maybe "never" prettyTime _feedLastUpdate
markFeedAsUnprocessed ∷ FeedStatus → FeedStatus
markFeedAsUnprocessed status =
status
{ _feedLastUpdate = Nothing
}
touchFeed ∷ FeedStatus → IO FeedStatus
touchFeed status = do
utcTime ← currentTime
return $ status{_feedLastUpdate = Just utcTime}
-- | Stateful information on a feed item
newtype FeedItemStatus = FeedItemStatus
{ _isProcessed ∷ Bool
}
deriving (Eq, Generic, Ord, Read, Show, Typeable)
instance Pretty FeedItemStatus where
pretty FeedItemStatus{..} = pretty _isProcessed
feedItemStatusOptions ∷ Options
feedItemStatusOptions =
defaultOptions
{ fieldLabelModifier = camelTo2 '_'
, omitNothingFields = True
}
instance ToJSON FeedItemStatus where
toJSON = genericToJSON feedItemStatusOptions
toEncoding = genericToEncoding feedItemStatusOptions
instance FromJSON FeedItemStatus where
parseJSON = genericParseJSON feedItemStatusOptions
markItemAsProcessed ∷ FeedItemStatus → FeedItemStatus
markItemAsProcessed status = status{_isProcessed = True}
markItemAsUnprocessed ∷ FeedItemStatus → FeedItemStatus
markItemAsUnprocessed status = status{_isProcessed = False}
-- | Consistent record of information for a single feed
data FeedRecord status = FeedRecord
{ _feedKey ∷ StatusUID status
, _feedLocation ∷ FeedLocation
, _feedDefinition ∷ FeedDefinition
, _feedStatus ∷ FeedStatus
}
deriving instance Eq (StatusUID s) ⇒ Eq (FeedRecord s)
deriving instance Ord (StatusUID s) ⇒ Ord (FeedRecord s)
deriving instance Show (StatusUID s) ⇒ Show (FeedRecord s)
instance Pretty (PrettyKey (FeedRecord Inserted)) where
pretty (PrettyKey record) = pretty $ _feedKey record
instance Pretty (PrettyKey (FeedRecord NotInserted)) where
pretty (PrettyKey record) = pretty $ _feedLocation record
instance Pretty (PrettyName (FeedRecord s)) where
pretty (PrettyName record) = if Text.null title then location else definition
where
title = record & _feedDefinition & _feedTitle
definition = prettyName $ _feedDefinition record
location = feedUri & uriAuthority <&> authorityHost <&> hostBS & fromMaybe "unknown" & decodeUtf8 & pretty @Text
FeedLocation feedUri _alternateTitle = _feedLocation record
mkFeedRecord ∷ FeedLocation → FeedDefinition → FeedStatus → FeedRecord NotInserted
mkFeedRecord = FeedRecord ()
-- | Consistent record of information for a single feed item
data FeedItemRecord status = FeedItemRecord
{ _itemKey ∷ StatusUID status
, _itemFeedKey ∷ UID
, _itemDefinition ∷ FeedItem
, _itemStatus ∷ FeedItemStatus
}
deriving instance Eq (StatusUID s) ⇒ Eq (FeedItemRecord s)
deriving instance Ord (StatusUID s) ⇒ Ord (FeedItemRecord s)
deriving instance Show (StatusUID s) ⇒ Show (FeedItemRecord s)
instance Pretty (PrettyKey (FeedItemRecord Inserted)) where
pretty (PrettyKey record) = pretty $ _itemKey record
instance Pretty (PrettyName (FeedItemRecord a)) where
pretty (PrettyName record) = pretty $ _itemTitle $ _itemDefinition record
prettyItemRecord ∷ Pretty (StatusUID s) ⇒ FeedItemRecord s → Doc AnsiStyle
prettyItemRecord FeedItemRecord{..} =
pretty _itemKey
<+> magenta (pretty _itemFeedKey)
<++> pretty _itemDefinition
<++> pretty _itemStatus
mkFeedItemRecord ∷ UID → FeedItem → FeedItemStatus → FeedItemRecord NotInserted
mkFeedItemRecord = FeedItemRecord ()
data Inserted
data NotInserted
type family StatusUID s where
StatusUID Inserted = UID
StatusUID NotInserted = ()