{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Text.RSS.Types where
import Control.Exception.Safe
import Data.Semigroup
import Data.Set
import Data.Text (Text, unpack)
import Data.Time.Clock
import Data.Time.LocalTime ()
import Data.Version
import GHC.Generics hiding ((:+:))
import Text.Read
import URI.ByteString
data RssException = InvalidBool Text
| InvalidDay Text
| InvalidHour Int
| InvalidInt Text
| InvalidURI URIParseError
| InvalidVersion Text
| InvalidProtocol Text
| InvalidTime Text
| MissingElement Text
deriving instance Eq RssException
deriving instance Generic RssException
deriving instance Read RssException
deriving instance Show RssException
instance Exception RssException where
displayException (InvalidBool t) = "Invalid bool: " ++ unpack t
displayException (InvalidDay t) = "Invalid day: " ++ unpack t
displayException (InvalidHour i) = "Invalid hour: " ++ show i
displayException (InvalidInt t) = "Invalid int: " ++ unpack t
displayException (InvalidURI t) = "Invalid URI reference: " ++ show t
displayException (InvalidVersion t) = "Invalid version: " ++ unpack t
displayException (InvalidProtocol t) = "Invalid Protocol: expected \"xml-rpc\", \"soap\" or \"http-post\", got \"" ++ unpack t ++ "\""
displayException (InvalidTime t) = "Invalid time: " ++ unpack t
displayException (MissingElement t) = "Missing element: " ++ unpack t
data RssURI = forall a . RssURI (URIRef a)
instance Eq RssURI where
RssURI a@URI{} == RssURI b@URI{} = a == b
RssURI a@RelativeRef{} == RssURI b@RelativeRef{} = a == b
_ == _ = False
instance Ord RssURI where
RssURI a@URI{} `compare` RssURI b@URI{} = a `compare` b
RssURI a@RelativeRef{} `compare` RssURI b@RelativeRef{} = a `compare` b
RssURI a@RelativeRef{} `compare` RssURI b@URI{} = LT
_ `compare` _ = GT
instance Show RssURI where
show (RssURI a@URI{}) = show a
show (RssURI a@RelativeRef{}) = show a
withRssURI :: (forall a . URIRef a -> b) -> RssURI -> b
withRssURI f (RssURI a) = f a
data RssCategory = RssCategory
{ categoryDomain :: Text
, categoryName :: Text
}
deriving instance Eq RssCategory
deriving instance Generic RssCategory
deriving instance Ord RssCategory
deriving instance Show RssCategory
data RssEnclosure = RssEnclosure
{ enclosureUrl :: RssURI
, enclosureLength :: Int
, enclosureType :: Text
}
deriving instance Eq RssEnclosure
deriving instance Generic RssEnclosure
deriving instance Ord RssEnclosure
deriving instance Show RssEnclosure
data RssSource = RssSource
{ sourceUrl :: RssURI
, sourceName :: Text
}
deriving instance Eq RssSource
deriving instance Generic RssSource
deriving instance Ord RssSource
deriving instance Show RssSource
data RssGuid = GuidText Text | GuidUri RssURI
deriving(Eq, Generic, Ord, Show)
data RssItem extensions = RssItem
{ itemTitle :: Text
, itemLink :: Maybe RssURI
, itemDescription :: Text
, itemAuthor :: Text
, itemCategories :: [RssCategory]
, itemComments :: Maybe RssURI
, itemEnclosure :: [RssEnclosure]
, itemGuid :: Maybe RssGuid
, itemPubDate :: Maybe UTCTime
, itemSource :: Maybe RssSource
, itemExtensions :: RssItemExtension extensions
}
deriving instance (Eq (RssItemExtension e)) => Eq (RssItem e)
deriving instance (Generic (RssItemExtension e)) => Generic (RssItem e)
deriving instance (Ord (RssItemExtension e)) => Ord (RssItem e)
deriving instance (Show (RssItemExtension e)) => Show (RssItem e)
type RssItem' = RssItem NoExtensions
data RssTextInput = RssTextInput
{ textInputTitle :: Text
, textInputDescription :: Text
, textInputName :: Text
, textInputLink :: RssURI
}
deriving instance Eq RssTextInput
deriving instance Generic RssTextInput
deriving instance Ord RssTextInput
deriving instance Show RssTextInput
data CloudProtocol = ProtocolXmlRpc | ProtocolSoap | ProtocolHttpPost
deriving(Eq, Generic, Ord, Read, Show)
data RssCloud = RssCloud
{ cloudUri :: RssURI
, cloudRegisterProcedure :: Text
, cloudProtocol :: CloudProtocol
}
deriving instance Eq RssCloud
deriving instance Generic RssCloud
deriving instance Ord RssCloud
deriving instance Show RssCloud
data RssImage = RssImage
{ imageUri :: RssURI
, imageTitle :: Text
, imageLink :: RssURI
, imageWidth :: Maybe Int
, imageHeight :: Maybe Int
, imageDescription :: Text
}
deriving instance Eq RssImage
deriving instance Generic RssImage
deriving instance Ord RssImage
deriving instance Show RssImage
newtype Hour = Hour Int
deriving(Eq, Generic, Ord, Read, Show)
instance Bounded Hour where
minBound = Hour 0
maxBound = Hour 23
instance Enum Hour where
fromEnum (Hour h) = fromEnum h
toEnum i = if i >= 0 && i < 24 then Hour i else error $ "Invalid hour: " <> show i
asHour :: MonadThrow m => Int -> m Hour
asHour i
| i >= 0 && i < 24 = return $ Hour i
| otherwise = throwM $ InvalidHour i
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
deriving(Bounded, Enum, Eq, Generic, Ord, Read, Show)
asDay :: MonadThrow m => Text -> m Day
asDay t = maybe (throwM $ InvalidDay t) return . readMaybe $ unpack t
data RssDocument extensions = RssDocument
{ documentVersion :: Version
, channelTitle :: Text
, channelLink :: RssURI
, channelDescription :: Text
, channelItems :: [RssItem extensions]
, channelLanguage :: Text
, channelCopyright :: Text
, channelManagingEditor :: Text
, channelWebmaster :: Text
, channelPubDate :: Maybe UTCTime
, channelLastBuildDate :: Maybe UTCTime
, channelCategories :: [RssCategory]
, channelGenerator :: Text
, channelDocs :: Maybe RssURI
, channelCloud :: Maybe RssCloud
, channelTtl :: Maybe Int
, channelImage :: Maybe RssImage
, channelRating :: Text
, channelTextInput :: Maybe RssTextInput
, channelSkipHours :: Set Hour
, channelSkipDays :: Set Day
, channelExtensions :: RssChannelExtension extensions
}
deriving instance (Eq (RssChannelExtension e), Eq (RssItemExtension e)) => Eq (RssDocument e)
deriving instance (Generic (RssChannelExtension e), Generic (RssItemExtension e)) => Generic (RssDocument e)
deriving instance (Ord (RssChannelExtension e), Ord (RssItemExtension e)) => Ord (RssDocument e)
deriving instance (Show (RssChannelExtension e), Show (RssItemExtension e)) => Show (RssDocument e)
type RssDocument' = RssDocument NoExtensions
data family RssChannelExtension extensionTag :: *
data family RssItemExtension extensionTag :: *
data NoExtensions = NoExtensions
deriving(Eq, Generic, Ord, Read, Show)
data instance RssChannelExtension NoExtensions = NoChannelExtensions
deriving(Eq, Generic, Ord, Read, Show)
data instance RssItemExtension NoExtensions = NoItemExtensions
deriving(Eq, Generic, Ord, Read, Show)