{-# LANGUAGE FlexibleContexts, OverloadedStrings #-}

-- | Module    : Network.MPD.Commands.Parse
-- Copyright   : (c) Ben Sinclair 2005-2009, Joachim Fasting 2010
-- License     : MIT (see LICENSE)
-- Maintainer  : Joachim Fasting <joachifm@fastmail.fm>
-- Stability   : alpha
--
-- Parsers for MPD data types.

module Network.MPD.Commands.Parse where

import           Network.MPD.Commands.Types

import           Control.Monad.Except
import           Data.Maybe (fromMaybe)

import           Network.MPD.Util

import           Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.UTF8 as UTF8

-- | Builds a 'Count' instance from an assoc. list.
parseCount :: [ByteString] -> Either String Count
parseCount :: [ByteString] -> Either String Count
parseCount = forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM Count -> (ByteString, ByteString) -> Either String Count
f forall a. Default a => a
def forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [(ByteString, ByteString)]
toAssocList
        where f :: Count -> (ByteString, ByteString) -> Either String Count
              f :: Count -> (ByteString, ByteString) -> Either String Count
f Count
a (ByteString
"songs", ByteString
x)    = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                    (\Seconds
x' -> Count
a { cSongs :: Seconds
cSongs = Seconds
x'}) Count
a ByteString
x
              f Count
a (ByteString
"playtime", ByteString
x) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                    (\Seconds
x' -> Count
a { cPlaytime :: Seconds
cPlaytime = Seconds
x' }) Count
a ByteString
x
              f Count
_ (ByteString, ByteString)
x               = forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show (ByteString, ByteString)
x

-- | Builds a list of 'Device' instances from an assoc. list
parseOutputs :: [ByteString] -> Either String [Device]
parseOutputs :: [ByteString] -> Either String [Device]
parseOutputs = forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM forall {a}.
(Eq a, IsString a, Show a) =>
Device -> (a, ByteString) -> Either String Device
f forall a. Default a => a
def)
             forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString]
-> [(ByteString, ByteString)] -> [[(ByteString, ByteString)]]
splitGroups [ByteString
"outputid"]
             forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [(ByteString, ByteString)]
toAssocList
    where f :: Device -> (a, ByteString) -> Either String Device
f Device
a (a
"outputid", ByteString
x)      = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                     (\Int
x' -> Device
a { dOutputID :: Int
dOutputID = Int
x' }) Device
a ByteString
x
          f Device
a (a
"outputname", ByteString
x)    = forall (m :: * -> *) a. Monad m => a -> m a
return Device
a { dOutputName :: String
dOutputName = ByteString -> String
UTF8.toString ByteString
x }
          f Device
a (a
"outputenabled", ByteString
x) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Bool
parseBool
                                     (\Bool
x' -> Device
a { dOutputEnabled :: Bool
dOutputEnabled = Bool
x'}) Device
a ByteString
x
          f Device
_ (a, ByteString)
x                    = forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show (a, ByteString)
x

-- | Builds a 'Stats' instance from an assoc. list.
parseStats :: [ByteString] -> Either String Stats
parseStats :: [ByteString] -> Either String Stats
parseStats = forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM forall {a}.
(Eq a, IsString a, Show a) =>
Stats -> (a, ByteString) -> Either String Stats
f forall a. Default a => a
def forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [(ByteString, ByteString)]
toAssocList
    where
        f :: Stats -> (a, ByteString) -> Either String Stats
f Stats
a (a
"artists", ByteString
x)     = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsArtists :: Seconds
stsArtists  = Seconds
x' }) Stats
a ByteString
x
        f Stats
a (a
"albums", ByteString
x)      = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsAlbums :: Seconds
stsAlbums   = Seconds
x' }) Stats
a ByteString
x
        f Stats
a (a
"songs", ByteString
x)       = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsSongs :: Seconds
stsSongs    = Seconds
x' }) Stats
a ByteString
x
        f Stats
a (a
"uptime", ByteString
x)      = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsUptime :: Seconds
stsUptime   = Seconds
x' }) Stats
a ByteString
x
        f Stats
a (a
"playtime", ByteString
x)    = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsPlaytime :: Seconds
stsPlaytime = Seconds
x' }) Stats
a ByteString
x
        f Stats
a (a
"db_playtime", ByteString
x) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsDbPlaytime :: Seconds
stsDbPlaytime = Seconds
x' }) Stats
a ByteString
x
        f Stats
a (a
"db_update", ByteString
x)   = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsDbUpdate :: Seconds
stsDbUpdate = Seconds
x' }) Stats
a ByteString
x
        f Stats
_ (a, ByteString)
x = forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show (a, ByteString)
x

parseMaybeSong :: [ByteString] -> Either String (Maybe Song)
parseMaybeSong :: [ByteString] -> Either String (Maybe Song)
parseMaybeSong [ByteString]
xs | forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ByteString]
xs   = forall a b. b -> Either a b
Right forall a. Maybe a
Nothing
                  | Bool
otherwise = forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ([(ByteString, ByteString)] -> Either String Song
parseSong forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [(ByteString, ByteString)]
toAssocList) [ByteString]
xs

-- | Builds a 'Song' instance from an assoc. list.
parseSong :: [(ByteString, ByteString)] -> Either String Song
parseSong :: [(ByteString, ByteString)] -> Either String Song
parseSong [(ByteString, ByteString)]
xs = case [(ByteString, ByteString)]
xs of
    (ByteString
"file", ByteString
path):[(ByteString, ByteString)]
ys -> forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM Song -> (ByteString, ByteString) -> Either String Song
f (Path -> Song
defaultSong (ByteString -> Path
Path ByteString
path)) [(ByteString, ByteString)]
ys
    [(ByteString, ByteString)]
_ -> forall a b. a -> Either a b
Left String
"Got a song without a file path! This indicates a bug in either libmpd-haskell or MPD itself!"

    where
        f :: Song -> (ByteString, ByteString) -> Either String Song
        f :: Song -> (ByteString, ByteString) -> Either String Song
f Song
s (ByteString
"Last-Modified", ByteString
v) =
            forall (m :: * -> *) a. Monad m => a -> m a
return Song
s { sgLastModified :: Maybe UTCTime
sgLastModified = forall t. ParseTime t => ByteString -> Maybe t
parseIso8601 ByteString
v }
        f Song
s (ByteString
"Time", ByteString
v) =
            forall (m :: * -> *) a. Monad m => a -> m a
return Song
s { sgLength :: Seconds
sgLength = forall a. a -> Maybe a -> a
fromMaybe Seconds
0 forall a b. (a -> b) -> a -> b
$ forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum ByteString
v }
        f Song
s (ByteString
"Id", ByteString
v) =
            forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum (\Int
v' -> Song
s { sgId :: Maybe Id
sgId = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Int -> Id
Id Int
v' }) Song
s ByteString
v
        f Song
s (ByteString
"Pos", ByteString
v) =
            forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                  (\Int
v' -> Song
s { sgIndex :: Maybe Int
sgIndex = forall a. a -> Maybe a
Just Int
v' }) Song
s ByteString
v)
                  (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return Song
s)
                  (Song -> Maybe Int
sgIndex Song
s)
        f Song
s (ByteString
k, ByteString
v) = forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. b -> (a -> b) -> Maybe a -> b
maybe Song
s (\Metadata
m -> Metadata -> Value -> Song -> Song
sgAddTag Metadata
m (ByteString -> Value
Value ByteString
v) Song
s) forall a b. (a -> b) -> a -> b
$
                     forall {a}. (Eq a, IsString a) => a -> Maybe Metadata
readMeta ByteString
k

        -- Custom-made Read instance
        readMeta :: a -> Maybe Metadata
readMeta a
"Artist" = forall a. a -> Maybe a
Just Metadata
Artist
        readMeta a
"ArtistSort" = forall a. a -> Maybe a
Just Metadata
ArtistSort
        readMeta a
"Album" = forall a. a -> Maybe a
Just Metadata
Album
        readMeta a
"AlbumSort" = forall a. a -> Maybe a
Just Metadata
AlbumSort
        readMeta a
"AlbumArtist" = forall a. a -> Maybe a
Just Metadata
AlbumArtist
        readMeta a
"AlbumArtistSort" = forall a. a -> Maybe a
Just Metadata
AlbumArtistSort
        readMeta a
"Title" = forall a. a -> Maybe a
Just Metadata
Title
        readMeta a
"Track" = forall a. a -> Maybe a
Just Metadata
Track
        readMeta a
"Name" = forall a. a -> Maybe a
Just Metadata
Name
        readMeta a
"Genre" = forall a. a -> Maybe a
Just Metadata
Genre
        readMeta a
"Date" = forall a. a -> Maybe a
Just Metadata
Date
        readMeta a
"OriginalDate" = forall a. a -> Maybe a
Just Metadata
OriginalDate
        readMeta a
"Composer" = forall a. a -> Maybe a
Just Metadata
Composer
        readMeta a
"Performer" = forall a. a -> Maybe a
Just Metadata
Performer
        readMeta a
"Conductor" = forall a. a -> Maybe a
Just Metadata
Conductor
        readMeta a
"Work" = forall a. a -> Maybe a
Just Metadata
Work
        readMeta a
"Grouping" = forall a. a -> Maybe a
Just Metadata
Grouping
        readMeta a
"Comment" = forall a. a -> Maybe a
Just Metadata
Comment
        readMeta a
"Disc" = forall a. a -> Maybe a
Just Metadata
Disc
        readMeta a
"Label" = forall a. a -> Maybe a
Just Metadata
Label
        readMeta a
"MUSICBRAINZ_ARTISTID" = forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_ARTISTID
        readMeta a
"MUSICBRAINZ_ALBUMID" = forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_ALBUMID
        readMeta a
"MUSICBRAINZ_ALBUMARTISTID" = forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_ALBUMARTISTID
        readMeta a
"MUSICBRAINZ_TRACKID" = forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_TRACKID
        readMeta a
"MUSICBRAINZ_RELEASETRACKID" = forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_RELEASETRACKID
        readMeta a
"MUSICBRAINZ_WORKID" = forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_WORKID
        readMeta a
_ = forall a. Maybe a
Nothing

-- | A helper that runs a parser on a string and, depending on the
-- outcome, either returns the result of some command applied to the
-- result, or a default value. Used when building structures.
parse :: (ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse :: forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe a
parser a -> b
f b
x = forall b a. b -> (a -> b) -> Maybe a -> b
maybe b
x a -> b
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Maybe a
parser

-- | A helper for running a parser returning Maybe on a pair of strings.
-- Returns Just if both strings where parsed successfully, Nothing otherwise.
pair :: (ByteString -> Maybe a) -> (ByteString, ByteString) -> Maybe (a, a)
pair :: forall a.
(ByteString -> Maybe a) -> (ByteString, ByteString) -> Maybe (a, a)
pair ByteString -> Maybe a
p (ByteString
x, ByteString
y) = case (ByteString -> Maybe a
p ByteString
x, ByteString -> Maybe a
p ByteString
y) of
                    (Just a
a, Just a
b) -> forall a. a -> Maybe a
Just (a
a, a
b)
                    (Maybe a, Maybe a)
_                -> forall a. Maybe a
Nothing