spoty-0.1.0.0: src/Utils/Spoty/Types.hs
{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, OverloadedStrings, TemplateHaskell #-}
{-|
Object declarations and lenses. Should not be imported by user code.
Please view the official documentation.
Note that the distinction between full and simple objects is implemented as an optional Maybe field with details.
-}
module Utils.Spoty.Types where
import Control.Applicative ((<$>), (<*>))
import Control.Lens (makeFields)
import Control.Monad (MonadPlus(..), mzero)
import Data.Aeson
import Data.Aeson.Types (Parser)
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
type URL = T.Text
type SpotID = T.Text
type SpotURI = T.Text
-- | Require that a field is present before parsing the corresponding value.
require :: FromJSON a => T.Text -> HM.HashMap T.Text Value -> Parser (Maybe a)
require str obj =
if HM.member str obj
then fmap Just (parseJSON $ Object obj)
else return Nothing
-- | Parse a map of key-value entries, wrapped in the given constructor.
parseStrMap :: MonadPlus m => HM.HashMap k Value -> (k -> T.Text -> a) -> m [a]
parseStrMap vals constr = sequence . flip map (HM.toList vals) $ \e ->
case e of
(key, String val) -> return $ constr key val
_ -> mzero
data ExternalID
= ExternalID
{
_idType :: T.Text,
_idIdentifier :: T.Text
}
deriving (Eq, Ord, Show)
makeFields ''ExternalID
instance FromJSON [ExternalID] where
parseJSON (Object v) = parseStrMap v ExternalID
parseJSON _ = mzero
data ExternalURL
= ExternalURL
{
_urlType :: T.Text,
_url :: URL
}
deriving (Eq, Ord, Show)
makeFields ''ExternalURL
instance FromJSON [ExternalURL] where
parseJSON (Object v) = parseStrMap v ExternalURL
parseJSON _ = mzero
data Image
= Image
{
_imageHeight :: Maybe Int,
_imagePath :: URL,
_imageWidth :: Maybe Int
}
deriving (Eq, Ord, Show)
makeFields ''Image
instance FromJSON Image where
parseJSON (Object v) = Image <$>
v .:? "height" <*>
v .: "url" <*>
v .:? "width"
parseJSON _ = mzero
data Paging a
= Paging
{
_pagingHref :: T.Text,
_pagingItems :: [a],
_pagingLimit :: Int,
_pagingNext :: Maybe URL,
_pagingOffset :: Int,
_pagingPrevious :: Maybe URL,
_pagingTotal :: Int
}
deriving (Eq, Ord, Show)
makeFields ''Paging
instance FromJSON a => FromJSON (Paging a) where
parseJSON (Object v) = Paging <$>
v .: "href" <*>
v .: "items" <*>
v .: "limit" <*>
v .:? "next" <*>
v .: "offset" <*>
v .:? "previous" <*>
v .: "total"
parseJSON _ = mzero
data User
= User
{
_userExternalUrls :: [ExternalURL],
_userHref :: URL,
_userSpotifyID :: SpotID,
_userSpotifyURI :: SpotURI
}
deriving (Eq, Ord, Show)
makeFields ''User
instance FromJSON User where
parseJSON (Object v) = User <$>
v .: "external_urls" <*>
v .: "href" <*>
v .: "id" <*>
v .: "uri"
parseJSON _ = mzero
data ArtistDetails
= ArtistDetails
{
_artistGenres :: [T.Text],
_artistImages :: [Image],
_artistPopularity :: Int
}
deriving (Eq, Ord, Show)
makeFields ''ArtistDetails
instance FromJSON ArtistDetails where
parseJSON (Object v) = ArtistDetails <$>
v .: "genres" <*>
v .: "images" <*>
v .: "popularity"
parseJSON _ = mzero
data Artist
= Artist
{
_artistExternalUrls :: [ExternalURL],
_artistHref :: URL,
_artistSpotifyID :: SpotID,
_artistName :: T.Text,
_artistSpotifyURI :: SpotURI,
_artistDetails :: Maybe ArtistDetails
}
deriving (Eq, Ord, Show)
makeFields ''Artist
instance FromJSON Artist where
parseJSON (Object v) = Artist <$>
v .: "external_urls" <*>
v .: "href" <*>
v .: "id" <*>
v .: "name" <*>
v .: "uri" <*>
require "genres" v
parseJSON _ = mzero
data TrackDetails
= TrackDetails
{
_trackAvailableMarkets :: [T.Text],
_trackExternalIDs :: [ExternalID],
_trackPopularity :: Int
}
deriving (Eq, Ord, Show)
makeFields ''TrackDetails
instance FromJSON TrackDetails where
parseJSON (Object v) = TrackDetails <$>
v .: "available_markets" <*>
v .: "external_ids" <*>
v .: "popularity"
parseJSON _ = mzero
data Track
= Track
{
_trackArtists :: [Artist],
_trackDiscNumber :: Int,
_trackDurationMs :: Int,
_trackExplicit :: Bool,
_trackExternalUrls :: [ExternalURL],
_trackHref :: URL,
_trackSpotifyID :: SpotID,
_trackName :: T.Text,
_trackPreviewURL :: URL,
_trackNumber :: Int,
_trackSpotifyURI :: SpotURI,
_trackDetails :: Maybe TrackDetails
}
deriving (Eq, Ord, Show)
makeFields ''Track
instance FromJSON Track where
parseJSON (Object v) = Track <$>
v .: "artists" <*>
v .: "disc_number" <*>
v .: "duration_ms" <*>
v .: "explicit" <*>
v .: "external_urls" <*>
v .: "href" <*>
v .: "id" <*>
v .: "name" <*>
v .: "preview_url" <*>
v .: "track_number" <*>
v .: "uri" <*>
require "available_markets" v
parseJSON _ = mzero
data AlbumDetails
= AlbumDetails
{
_albumArtists :: [Artist],
_albumExternalIDs :: [ExternalID],
_albumGenres :: [T.Text],
_albumPopularity :: Int,
_albumReleaseDate :: T.Text,
_albumReleaseDatePrecision :: T.Text,
_albumTracks :: Paging Track
}
deriving (Eq, Ord, Show)
makeFields ''AlbumDetails
instance FromJSON AlbumDetails where
parseJSON (Object v) = AlbumDetails <$>
v .: "artists" <*>
v .: "external_ids" <*>
v .: "genres" <*>
v .: "popularity" <*>
v .: "release_date" <*>
v .: "release_date_precision" <*>
v .: "tracks"
parseJSON _ = mzero
data Album
= Album
{
_albumType :: T.Text,
_albumAvailableMarkets :: [T.Text],
_albumExternalURLs :: [ExternalURL],
_albumHref :: T.Text,
_albumSpotifyID :: SpotID,
_albumImages :: [Image],
_albumName :: T.Text,
_albumSpotifyURI :: SpotURI,
_albumDetails :: Maybe AlbumDetails
}
deriving (Eq, Ord, Show)
makeFields ''Album
instance FromJSON Album where
parseJSON (Object v) = Album <$>
v .: "album_type" <*>
v .: "available_markets" <*>
v .: "external_urls" <*>
v .: "href" <*>
v .: "id" <*>
v .: "images" <*>
v .: "name" <*>
v .: "uri" <*>
require "artists" v
parseJSON _ = mzero