{-# 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
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 = (Count -> (ByteString, ByteString) -> Either String Count)
-> Count -> [(ByteString, ByteString)] -> Either String Count
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 Count
forall a. Default a => a
def ([(ByteString, ByteString)] -> Either String Count)
-> ([ByteString] -> [(ByteString, ByteString)])
-> [ByteString]
-> Either String Count
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)    = Count -> Either String Count
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Count -> Either String Count) -> Count -> Either String Count
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Count) -> Count -> ByteString -> Count
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                    (\Seconds
x' -> Count
a { cSongs = x'}) Count
a ByteString
x
              f Count
a (ByteString
"playtime", ByteString
x) = Count -> Either String Count
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Count -> Either String Count) -> Count -> Either String Count
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Count) -> Count -> ByteString -> Count
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                    (\Seconds
x' -> Count
a { cPlaytime = x' }) Count
a ByteString
x
              f Count
_ (ByteString, ByteString)
x               = String -> Either String Count
forall a b. a -> Either a b
Left (String -> Either String Count) -> String -> Either String Count
forall a b. (a -> b) -> a -> b
$ (ByteString, ByteString) -> String
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 = ([(ByteString, ByteString)] -> Either String Device)
-> [[(ByteString, ByteString)]] -> Either String [Device]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM ((Device -> (ByteString, ByteString) -> Either String Device)
-> Device -> [(ByteString, ByteString)] -> Either String Device
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM Device -> (ByteString, ByteString) -> Either String Device
forall {a}.
(Eq a, IsString a, Show a) =>
Device -> (a, ByteString) -> Either String Device
f Device
forall a. Default a => a
def)
             ([[(ByteString, ByteString)]] -> Either String [Device])
-> ([ByteString] -> [[(ByteString, ByteString)]])
-> [ByteString]
-> Either String [Device]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString]
-> [(ByteString, ByteString)] -> [[(ByteString, ByteString)]]
splitGroups [ByteString
"outputid"]
             ([(ByteString, ByteString)] -> [[(ByteString, ByteString)]])
-> ([ByteString] -> [(ByteString, ByteString)])
-> [ByteString]
-> [[(ByteString, ByteString)]]
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)      = Device -> Either String Device
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Device -> Either String Device) -> Device -> Either String Device
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Int)
-> (Int -> Device) -> Device -> ByteString -> Device
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Int
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                     (\Int
x' -> Device
a { dOutputID = x' }) Device
a ByteString
x
          f Device
a (a
"outputname", ByteString
x)    = Device -> Either String Device
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return Device
a { dOutputName = UTF8.toString x }
          f Device
a (a
"outputenabled", ByteString
x) = Device -> Either String Device
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Device -> Either String Device) -> Device -> Either String Device
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Bool)
-> (Bool -> Device) -> Device -> ByteString -> Device
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Bool
parseBool
                                     (\Bool
x' -> Device
a { dOutputEnabled = x'}) Device
a ByteString
x
          f Device
_ (a, ByteString)
x                    = String -> Either String Device
forall a b. a -> Either a b
Left (String -> Either String Device) -> String -> Either String Device
forall a b. (a -> b) -> a -> b
$ (a, ByteString) -> String
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 = (Stats -> (ByteString, ByteString) -> Either String Stats)
-> Stats -> [(ByteString, ByteString)] -> Either String Stats
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM Stats -> (ByteString, ByteString) -> Either String Stats
forall {a}.
(Eq a, IsString a, Show a) =>
Stats -> (a, ByteString) -> Either String Stats
f Stats
forall a. Default a => a
def ([(ByteString, ByteString)] -> Either String Stats)
-> ([ByteString] -> [(ByteString, ByteString)])
-> [ByteString]
-> Either String Stats
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)     = Stats -> Either String Stats
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats -> Either String Stats) -> Stats -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Stats) -> Stats -> ByteString -> Stats
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsArtists  = x' }) Stats
a ByteString
x
        f Stats
a (a
"albums", ByteString
x)      = Stats -> Either String Stats
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats -> Either String Stats) -> Stats -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Stats) -> Stats -> ByteString -> Stats
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsAlbums   = x' }) Stats
a ByteString
x
        f Stats
a (a
"songs", ByteString
x)       = Stats -> Either String Stats
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats -> Either String Stats) -> Stats -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Stats) -> Stats -> ByteString -> Stats
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsSongs    = x' }) Stats
a ByteString
x
        f Stats
a (a
"uptime", ByteString
x)      = Stats -> Either String Stats
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats -> Either String Stats) -> Stats -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Stats) -> Stats -> ByteString -> Stats
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsUptime   = x' }) Stats
a ByteString
x
        f Stats
a (a
"playtime", ByteString
x)    = Stats -> Either String Stats
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats -> Either String Stats) -> Stats -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Stats) -> Stats -> ByteString -> Stats
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsPlaytime = x' }) Stats
a ByteString
x
        f Stats
a (a
"db_playtime", ByteString
x) = Stats -> Either String Stats
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats -> Either String Stats) -> Stats -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Stats) -> Stats -> ByteString -> Stats
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsDbPlaytime = x' }) Stats
a ByteString
x
        f Stats
a (a
"db_update", ByteString
x)   = Stats -> Either String Stats
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats -> Either String Stats) -> Stats -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Seconds)
-> (Seconds -> Stats) -> Stats -> ByteString -> Stats
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Seconds
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                 (\Seconds
x' -> Stats
a { stsDbUpdate = x' }) Stats
a ByteString
x
        f Stats
_ (a, ByteString)
x = String -> Either String Stats
forall a b. a -> Either a b
Left (String -> Either String Stats) -> String -> Either String Stats
forall a b. (a -> b) -> a -> b
$ (a, ByteString) -> String
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 | [ByteString] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ByteString]
xs   = Maybe Song -> Either String (Maybe Song)
forall a b. b -> Either a b
Right Maybe Song
forall a. Maybe a
Nothing
                  | Bool
otherwise = Song -> Maybe Song
forall a. a -> Maybe a
Just (Song -> Maybe Song)
-> Either String Song -> Either String (Maybe Song)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ([(ByteString, ByteString)] -> Either String Song
parseSong ([(ByteString, ByteString)] -> Either String Song)
-> ([ByteString] -> [(ByteString, ByteString)])
-> [ByteString]
-> Either String Song
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 -> (Song -> (ByteString, ByteString) -> Either String Song)
-> Song -> [(ByteString, ByteString)] -> Either String Song
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)]
_ -> String -> Either String Song
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) =
            Song -> Either String Song
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return Song
s { sgLastModified = parseIso8601 v }
        f Song
s (ByteString
"Time", ByteString
v) =
            Song -> Either String Song
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return Song
s { sgLength = fromMaybe 0 $ parseNum v }
        f Song
s (ByteString
"Id", ByteString
v) =
            Song -> Either String Song
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Song -> Either String Song) -> Song -> Either String Song
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Int)
-> (Int -> Song) -> Song -> ByteString -> Song
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Int
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum (\Int
v' -> Song
s { sgId = Just $ Id v' }) Song
s ByteString
v
        f Song
s (ByteString
"Pos", ByteString
v) =
            Either String Song
-> (Int -> Either String Song) -> Maybe Int -> Either String Song
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Song -> Either String Song
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Song -> Either String Song) -> Song -> Either String Song
forall a b. (a -> b) -> a -> b
$ (ByteString -> Maybe Int)
-> (Int -> Song) -> Song -> ByteString -> Song
forall a b.
(ByteString -> Maybe a) -> (a -> b) -> b -> ByteString -> b
parse ByteString -> Maybe Int
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum
                                  (\Int
v' -> Song
s { sgIndex = Just v' }) Song
s ByteString
v)
                  (Either String Song -> Int -> Either String Song
forall a b. a -> b -> a
const (Either String Song -> Int -> Either String Song)
-> Either String Song -> Int -> Either String Song
forall a b. (a -> b) -> a -> b
$ Song -> Either String Song
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return Song
s)
                  (Song -> Maybe Int
sgIndex Song
s)
        f Song
s (ByteString
k, ByteString
v) = Song -> Either String Song
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Song -> Either String Song)
-> (Maybe Metadata -> Song) -> Maybe Metadata -> Either String Song
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Song -> (Metadata -> Song) -> Maybe Metadata -> Song
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) (Maybe Metadata -> Either String Song)
-> Maybe Metadata -> Either String Song
forall a b. (a -> b) -> a -> b
$
                     ByteString -> Maybe Metadata
forall {a}. (Eq a, IsString a) => a -> Maybe Metadata
readMeta ByteString
k

        -- Custom-made Read instance
        readMeta :: a -> Maybe Metadata
readMeta a
"Artist" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Artist
        readMeta a
"ArtistSort" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
ArtistSort
        readMeta a
"Album" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Album
        readMeta a
"AlbumSort" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
AlbumSort
        readMeta a
"AlbumArtist" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
AlbumArtist
        readMeta a
"AlbumArtistSort" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
AlbumArtistSort
        readMeta a
"Title" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Title
        readMeta a
"Track" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Track
        readMeta a
"Name" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Name
        readMeta a
"Genre" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Genre
        readMeta a
"Date" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Date
        readMeta a
"OriginalDate" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
OriginalDate
        readMeta a
"Composer" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Composer
        readMeta a
"Performer" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Performer
        readMeta a
"Conductor" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Conductor
        readMeta a
"Work" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Work
        readMeta a
"Grouping" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Grouping
        readMeta a
"Comment" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Comment
        readMeta a
"Disc" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Disc
        readMeta a
"Label" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
Label
        readMeta a
"MUSICBRAINZ_ARTISTID" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_ARTISTID
        readMeta a
"MUSICBRAINZ_ALBUMID" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_ALBUMID
        readMeta a
"MUSICBRAINZ_ALBUMARTISTID" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_ALBUMARTISTID
        readMeta a
"MUSICBRAINZ_TRACKID" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_TRACKID
        readMeta a
"MUSICBRAINZ_RELEASETRACKID" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_RELEASETRACKID
        readMeta a
"MUSICBRAINZ_WORKID" = Metadata -> Maybe Metadata
forall a. a -> Maybe a
Just Metadata
MUSICBRAINZ_WORKID
        readMeta a
_ = Maybe Metadata
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 = b -> (a -> b) -> Maybe a -> b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe b
x a -> b
f (Maybe a -> b) -> (ByteString -> Maybe a) -> ByteString -> b
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) -> (a, a) -> Maybe (a, a)
forall a. a -> Maybe a
Just (a
a, a
b)
                    (Maybe a, Maybe a)
_                -> Maybe (a, a)
forall a. Maybe a
Nothing