4 changed files with 129 additions and 7 deletions
@ -0,0 +1,100 @@ |
|||
{-# LANGUAGE DeriveFunctor #-} |
|||
{-# LANGUAGE OverloadedStrings #-} |
|||
{-# LANGUAGE TupleSections #-} |
|||
|
|||
{- | |
|||
Module : Network.MPD.Applicative.Internal |
|||
Copyright : (c) Simon Hengel 2012 |
|||
License : MIT |
|||
|
|||
Maintainer : joachifm@fastmail.fm |
|||
Stability : stable |
|||
Portability : unportable |
|||
|
|||
Applicative MPD command interface. |
|||
|
|||
This allows us to combine commands into command lists, as in |
|||
|
|||
> (,,) <$> currentSong <*> stats <*> status |
|||
|
|||
where the requests are automatically combined into a command list and |
|||
the result of each command passed to the consumer. |
|||
-} |
|||
|
|||
module Internal |
|||
( Parser(..) |
|||
, liftParser |
|||
, getResponse |
|||
, emptyResponse |
|||
, unexpected |
|||
, Command(..) |
|||
, runCommand |
|||
) where |
|||
|
|||
import Control.Applicative |
|||
import Control.Monad |
|||
import Data.ByteString.Char8 (ByteString) |
|||
|
|||
import Network.MPD.Core hiding (getResponse) |
|||
import qualified Network.MPD.Core as Core |
|||
import Control.Monad.Error |
|||
|
|||
-- | A line-oriented parser that returns a value along with any remaining input. |
|||
newtype Parser a |
|||
= Parser { runParser :: [ByteString] -> Either String (a, [ByteString]) } |
|||
deriving Functor |
|||
|
|||
instance Monad Parser where |
|||
fail = Parser . const . Left |
|||
return a = Parser $ \input -> Right (a, input) |
|||
p1 >>= p2 = Parser $ \input -> runParser p1 input >>= uncurry (runParser . p2) |
|||
|
|||
instance Applicative Parser where |
|||
pure = return |
|||
(<*>) = ap |
|||
|
|||
-- | Convert a regular parser. |
|||
liftParser :: ([ByteString] -> Either String a) -> Parser a |
|||
liftParser p = Parser $ \input -> case break (== "list_OK") input of |
|||
(xs, ys) -> fmap (, drop 1 ys) (p xs) |
|||
|
|||
-- | Return everything until the next "list_OK". |
|||
getResponse :: Parser [ByteString] |
|||
getResponse = Parser $ \input -> case break (== "list_OK") input of |
|||
(xs, ys) -> Right (xs, drop 1 ys) |
|||
|
|||
-- | For commands returning an empty response. |
|||
emptyResponse :: Parser () |
|||
emptyResponse = do |
|||
r <- getResponse |
|||
unless (null r) $ |
|||
unexpected r |
|||
|
|||
-- | Fail with unexpected response. |
|||
unexpected :: [ByteString] -> Parser a |
|||
unexpected = fail . ("unexpected Response: " ++) . show |
|||
|
|||
-- | A compound command, comprising a parser for the responses and a |
|||
-- combined request of an arbitrary number of commands. |
|||
data Command a = Command { |
|||
commandParser :: Parser a |
|||
, commandRequest :: [String] |
|||
} deriving Functor |
|||
|
|||
instance Applicative Command where |
|||
pure a = Command (pure a) [] |
|||
(Command p1 c1) <*> (Command p2 c2) = Command (p1 <*> p2) (c1 ++ c2) |
|||
|
|||
-- | Execute a 'Command'. |
|||
runCommand :: MonadMPD m => Command a -> m a |
|||
runCommand (Command p c) = do |
|||
r <- Core.getResponse command |
|||
case runParser p r of |
|||
Left err -> throwError (Unexpected err) |
|||
Right (a, []) -> return a |
|||
Right (_, xs) -> throwError (Unexpected $ "superfluous input: " ++ show xs) |
|||
where |
|||
command = case c of |
|||
[x] -> x |
|||
xs -> unlines ("command_list_ok_begin" : xs) |
|||
++ "command_list_end" |
Reference in new issue