{-# LANGUAGE FlexibleContexts, OverloadedStrings #-} -- | Module : Network.MPD.Commands.Parse -- Copyright : (c) Ben Sinclair 2005-2009, Joachim Fasting 2010 -- License : MIT (see LICENSE) -- Maintainer : Joachim Fasting -- 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 = foldM f def . toAssocList where f :: Count -> (ByteString, ByteString) -> Either String Count f a ("songs", x) = return $ parse parseNum (\x' -> a { cSongs = x'}) a x f a ("playtime", x) = return $ parse parseNum (\x' -> a { cPlaytime = x' }) a x f _ x = Left $ show x -- | Builds a list of 'Device' instances from an assoc. list parseOutputs :: [ByteString] -> Either String [Device] parseOutputs = mapM (foldM f def) . splitGroups ["outputid"] . toAssocList where f a ("outputid", x) = return $ parse parseNum (\x' -> a { dOutputID = x' }) a x f a ("outputname", x) = return a { dOutputName = UTF8.toString x } f a ("outputenabled", x) = return $ parse parseBool (\x' -> a { dOutputEnabled = x'}) a x f _ x = Left $ show x -- | Builds a 'Stats' instance from an assoc. list. parseStats :: [ByteString] -> Either String Stats parseStats = foldM f def . toAssocList where f a ("artists", x) = return $ parse parseNum (\x' -> a { stsArtists = x' }) a x f a ("albums", x) = return $ parse parseNum (\x' -> a { stsAlbums = x' }) a x f a ("songs", x) = return $ parse parseNum (\x' -> a { stsSongs = x' }) a x f a ("uptime", x) = return $ parse parseNum (\x' -> a { stsUptime = x' }) a x f a ("playtime", x) = return $ parse parseNum (\x' -> a { stsPlaytime = x' }) a x f a ("db_playtime", x) = return $ parse parseNum (\x' -> a { stsDbPlaytime = x' }) a x f a ("db_update", x) = return $ parse parseNum (\x' -> a { stsDbUpdate = x' }) a x f _ x = Left $ show x parseMaybeSong :: [ByteString] -> Either String (Maybe Song) parseMaybeSong xs | null xs = Right Nothing | otherwise = Just <$> (parseSong . toAssocList) xs -- | Builds a 'Song' instance from an assoc. list. parseSong :: [(ByteString, ByteString)] -> Either String Song parseSong xs = case xs of ("file", path):ys -> foldM f (defaultSong (Path path)) ys _ -> Left "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 s ("Last-Modified", v) = return s { sgLastModified = parseIso8601 v } f s ("Time", v) = return s { sgLength = fromMaybe 0 $ parseNum v } f s ("Id", v) = return $ parse parseNum (\v' -> s { sgId = Just $ Id v' }) s v f s ("Pos", v) = maybe (return $ parse parseNum (\v' -> s { sgIndex = Just v' }) s v) (const $ return s) (sgIndex s) f s (k, v) = return . maybe s (\m -> sgAddTag m (Value v) s) $ readMeta k -- Custom-made Read instance readMeta "ArtistSort" = Just ArtistSort readMeta "Artist" = Just Artist readMeta "Album" = Just Album readMeta "AlbumArtist" = Just AlbumArtist readMeta "AlbumArtistSort" = Just AlbumArtistSort readMeta "Title" = Just Title readMeta "Genre" = Just Genre readMeta "Name" = Just Name readMeta "Composer" = Just Composer readMeta "Performer" = Just Performer readMeta "Comment" = Just Comment readMeta "Date" = Just Date readMeta "Track" = Just Track readMeta "Disc" = Just Disc readMeta "MUSICBRAINZ_ARTISTID" = Just MUSICBRAINZ_ARTISTID readMeta "MUSICBRAINZ_ALBUMID" = Just MUSICBRAINZ_ALBUMID readMeta "MUSICBRAINZ_ALBUMARTISTID" = Just MUSICBRAINZ_ALBUMARTISTID readMeta "MUSICBRAINZ_TRACKID" = Just MUSICBRAINZ_TRACKID readMeta "MUSICBRAINZ_RELEASETRACKID" = Just MUSICBRAINZ_RELEASETRACKID readMeta _ = 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 parser f x = maybe x f . 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 p (x, y) = case (p x, p y) of (Just a, Just b) -> Just (a, b) _ -> Nothing