libmpd 0.9.2.0 → 0.9.3.0
raw patch · 18 files changed
+146/−69 lines, 18 filesdep −old-localedep ~basedep ~mtldep ~time
Dependencies removed: old-locale
Dependency ranges changed: base, mtl, time
Files
- changelog.md +11/−3
- libmpd.cabal +6/−14
- src/Network/MPD/Applicative/ClientToClient.hs +0/−2
- src/Network/MPD/Applicative/Database.hs +0/−2
- src/Network/MPD/Applicative/Internal.hs +1/−3
- src/Network/MPD/Applicative/Stickers.hs +0/−2
- src/Network/MPD/Applicative/StoredPlaylists.hs +0/−2
- src/Network/MPD/Commands/CurrentPlaylist.hs +1/−1
- src/Network/MPD/Commands/Extensions.hs +0/−1
- src/Network/MPD/Commands/Parse.hs +1/−2
- src/Network/MPD/Commands/Query.hs +112/−11
- src/Network/MPD/Commands/Types.hs +3/−3
- src/Network/MPD/Core.hs +4/−5
- src/Network/MPD/Core/Class.hs +1/−1
- src/Network/MPD/Core/Error.hs +0/−5
- src/Network/MPD/Util.hs +3/−7
- tests/ParserSpec.hs +0/−2
- tests/StringConn.hs +3/−3
changelog.md view
@@ -1,15 +1,23 @@-* v0.9.2.0+* v0.9.3.0 2021-01-02+ - Drop support for GHC < 8.4, require base > 4.11.+ - Bump `cabal-version` to 2.4.+ - Add `Ord` instance for `PlaylistName`, `Path`, and `Value`+ - Add support for MPD 0.21 style filters to the `Query` type,+ see [Filters](https://www.musicpd.org/doc/html/protocol.html#filters) in the MPD protocol documentation. + Implemented through the added combinators `/=?`, `%?`, `~?`, `/~?`, `qNot`, `qModSince`, `qFile`, `qBase`.+ +* v0.9.2.0 2020-10-02 - New command: `seekCur` - Add `newtype Sign` to pass positive numbers to `MPDArg` with leading `+/-`. - Add monadic versions of `deleteRange` and `moveRange` commands (previously only had applicative versions) - Deprecate `<&>`, use `<>` instead. `<&>` will be removed in the next major version. -* v0.9.1.0+* v0.9.1.0 2020-01-27 - Support partition in Network.MPD.Status - Ignore unknown key-value pairs in Network.MPD.status so that it breaks much less often. -* v0.9.0.10+* v0.9.0.10 2019-10-06 - Port it for newer network library * v0.9.0, 2014-09-21
libmpd.cabal view
@@ -1,5 +1,6 @@+Cabal-Version: 2.4 Name: libmpd-Version: 0.9.2.0+Version: 0.9.3.0 Synopsis: An MPD client library. Description: A client library for MPD, the Music Player Daemon. Category: Network, Sound@@ -19,9 +20,8 @@ Homepage: http://github.com/vimus/libmpd-haskell#readme Bug-reports: http://github.com/vimus/libmpd-haskell/issues -Tested-With: GHC ==8.0.1, GHC==8.2.2, GHC==8.4.3, GHC==8.6.1+Tested-With: GHC ==8.4.4, GHC==8.6.5, GHC==8.8.4, GHC==8.10.2 Build-Type: Simple-Cabal-Version: >= 1.10 Extra-Source-Files: README.md@@ -39,14 +39,14 @@ Build-Depends: -- Platform dependencies- base >= 4.9 && < 5+ base >= 4.11 && < 5 , attoparsec >= 0.10.1 && < 1 , bytestring >= 0.9 && < 1 , containers >= 0.3 && < 1 , filepath >= 1 && < 2- , mtl >= 2.0 && < 3- , old-locale >= 1 && < 2+ , mtl >= 2.2.2 && < 3 , text >= 0.11 && < 2+ , time >= 1.5 && < 2 -- Additional dependencies , data-default-class >= 0.0.1 && < 1@@ -54,13 +54,6 @@ , safe-exceptions >= 0.1 && < 0.2 , utf8-string >= 0.3.1 && < 1.1 - if impl(ghc >= 7.10.0)- Build-Depends:- time >= 1.5- else- Build-Depends:- time >= 1.1 && <1.5- Exposed-Modules: Network.MPD , Network.MPD.Applicative@@ -121,7 +114,6 @@ , containers , filepath , mtl- , old-locale , text , time
src/Network/MPD/Applicative/ClientToClient.hs view
@@ -25,8 +25,6 @@ , sendMessage ) where -import Control.Applicative- import Network.MPD.Commands.Arg hiding (Command) import Network.MPD.Applicative.Internal import Network.MPD.Applicative.Util
src/Network/MPD/Applicative/Database.hs view
@@ -14,8 +14,6 @@ module Network.MPD.Applicative.Database where -import Control.Applicative- import qualified Network.MPD.Commands.Arg as Arg import Network.MPD.Commands.Arg hiding (Command) import Network.MPD.Commands.Parse
src/Network/MPD/Applicative/Internal.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-}-{-# LANGUAGE CPP #-} {- | Module : Network.MPD.Applicative.Internal@@ -32,13 +31,12 @@ , 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+import Control.Monad.Except import qualified Control.Monad.Fail as Fail -- | A line-oriented parser that returns a value along with any remaining input.
src/Network/MPD/Applicative/Stickers.hs view
@@ -26,8 +26,6 @@ import Network.MPD.Commands.Types import Network.MPD.Util -import Control.Applicative- import qualified Data.ByteString.UTF8 as UTF8 -- | Read sticker value for the object specified.
src/Network/MPD/Applicative/StoredPlaylists.hs view
@@ -32,8 +32,6 @@ import Network.MPD.Commands.Types import Network.MPD.Util -import Control.Applicative- -- | List song items in the playlist. listPlaylist :: PlaylistName -> Command [Path] listPlaylist plName = Command p ["listplaylist" <@> plName]
src/Network/MPD/Commands/CurrentPlaylist.hs view
@@ -47,7 +47,7 @@ import Network.MPD.Core import Network.MPD.Util -import Control.Monad.Error (throwError)+import Control.Monad.Except (throwError) -- | Like 'add', but returns a playlist id. addId :: MonadMPD m => Path -> Maybe Position -> m Id
src/Network/MPD/Commands/Extensions.hs view
@@ -23,7 +23,6 @@ import Control.Monad (liftM) import Data.Traversable (for) import Data.Foldable (for_)-import Data.Semigroup ((<>)) -- | This is exactly the same as `update`. updateId :: MonadMPD m => Maybe Path -> m Integer
src/Network/MPD/Commands/Parse.hs view
@@ -12,8 +12,7 @@ import Network.MPD.Commands.Types -import Control.Applicative-import Control.Monad.Error+import Control.Monad.Except import Data.Maybe (fromMaybe) import Network.MPD.Util
src/Network/MPD/Commands/Query.hs view
@@ -10,13 +10,11 @@ Query interface. -} -module Network.MPD.Commands.Query (Query, (=?), (<&>), anything) where+module Network.MPD.Commands.Query (Query, (=?) ,(/=?), (%?), (~?), (/~?), qNot, qModSince, qFile, qBase, (<&>), anything) where import Network.MPD.Commands.Arg import Network.MPD.Commands.Types--import Data.Monoid-import Data.Semigroup+import Data.Time (UTCTime,formatTime,defaultTimeLocale) -- | An interface for creating MPD queries. --@@ -30,33 +28,136 @@ -- is \"Bar\", we use: -- -- > Artist =? "Foo" <> Album =? "Bar"-newtype Query = Query [Match] deriving Show+data Query = Query [Match] | Filter Expr+ deriving Show -- A single query clause, comprising a metadata key and a desired value. data Match = Match Metadata Value instance Show Match where- show (Match meta query) = show meta ++ " \"" ++ toString query ++ "\""- showList xs _ = unwords $ map show xs+ show (Match meta query) = show meta ++ " \"" ++ toString query ++ "\""+ showList xs _ = unwords $ fmap show xs +data Expr = Exact Match+ | ExactNot Match+ | Contains Match+ | Regex Match+ | RegexNot Match+ | File Path+ | Base Path+ | ModifiedSince UTCTime+ | ExprNot Expr+ | ExprAnd Expr Expr+ | ExprEmpty+instance Show Expr where+ show (Exact (Match meta query)) = "(" ++ show meta ++ " == " +++ "\\\"" ++ toString query ++ "\\\"" ++ ")"+ show (ExactNot (Match meta query)) = "(" ++ show meta ++ " != " +++ "\\\"" ++ toString query ++ "\\\"" ++ ")"+ show (Contains (Match meta query)) = "(" ++ show meta ++ " contains " +++ "\\\"" ++ toString query ++ "\\\"" ++ ")"+ show (Regex (Match meta query)) = "(" ++ show meta ++ " =~ " +++ "\\\"" ++ toString query ++ "\\\"" ++ ")"+ show (RegexNot (Match meta query)) = "(" ++ show meta ++ " !~ " +++ "\\\"" ++ toString query ++ "\\\"" ++ ")"+ show (File file) = "(file == " ++ "\\\"" ++ toString file ++ "\\\"" ++ ")"+ show (Base dir) = "(base " ++ "\\\"" ++ toString dir ++ "\\\"" ++ ")"+ show (ModifiedSince time) = "(modified-since " ++ "\\\"" +++ formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%SZ" time +++ "\\\"" ++ ")"+ show (ExprNot expr) = "(!" ++ show expr ++ ")"+ show (ExprAnd e1 e2) = "(" ++ show e1 ++ " AND " ++ show e2 ++ ")"+ show ExprEmpty = ""++toExpr :: [Match] -> Expr+toExpr [] = ExprEmpty+toExpr (m:[]) = Exact m+toExpr (m:ms) = ExprAnd (Exact m) (toExpr ms)+ instance Monoid Query where mempty = Query []- Query a `mappend` Query b = Query (a ++ b)+ Query a `mappend` Query b = Query (a ++ b)+ Query [] `mappend` Filter b = Filter b+ Filter a `mappend` Query [] = Filter a+ Query a `mappend` Filter b = Filter (ExprAnd (toExpr a) b)+ Filter a `mappend` Query b = Filter (ExprAnd a (toExpr b))+ Filter a `mappend` Filter b = Filter (a <> b) instance Semigroup Query where (<>) = mappend+instance Semigroup Expr where+ ex1 <> ex2 = ExprAnd ex1 ex2 instance MPDArg Query where- prep = foldl (<++>) (Args []) . f- where f (Query ms) = map (\(Match m q) -> Args [show m] <++> q) ms+ prep (Query ms) = foldl (<++>) (Args [])+ (fmap (\(Match m q) -> Args [show m] <++> q) ms)+ prep (Filter expr) = Args ["\"" ++ show expr ++ "\""] -- | An empty query. Matches anything. anything :: Query anything = mempty --- | Create a query.+-- | Create a query matching a tag with a value. (=?) :: Metadata -> Value -> Query m =? s = Query [Match m s]++-- | Create a query matching a tag with anything but a value.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+(/=?) :: Metadata -> Value -> Query+m /=? s = Filter (ExactNot (Match m s))++-- | Create a query for a tag containing a value.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+(%?) :: Metadata -> Value -> Query+m %? s = Filter (Contains (Match m s))++-- | Create a query matching a tag with regexp.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+(~?) :: Metadata -> Value -> Query+m ~? s = Filter (Regex (Match m s))++-- | Create a query matching a tag with anything but a regexp.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+(/~?) :: Metadata -> Value -> Query+m /~? s = Filter (RegexNot (Match m s))++-- | Negate a Query.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+qNot :: Query -> Query+qNot (Query ms) = Filter (ExprNot (toExpr ms))+qNot (Filter (ExprNot ex)) = Filter ex+qNot (Filter ex) = Filter (ExprNot ex)++-- | Create a query for songs modified since a date.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+qModSince :: UTCTime -> Query+qModSince time = Filter (ModifiedSince time)++-- | Create a query for the full song URI relative to the music directory.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+qFile :: Path -> Query+qFile file = Filter (File file)++-- | Limit the query to the given directory, relative to the music directory.+-- Requires MPD 0.21 or newer.+--+-- @since 0.9.3.0+qBase :: Path -> Query+qBase dir = Filter (Base dir) -- | Combine queries. infixr 6 <&>
src/Network/MPD/Commands/Types.hs view
@@ -74,7 +74,7 @@ -- | Used for commands which require a playlist name. -- If empty, the current playlist is used. newtype PlaylistName = PlaylistName ByteString- deriving (Eq, Show, MPDArg)+ deriving (Eq, Show, Ord, MPDArg) instance ToString PlaylistName where toString (PlaylistName x) = UTF8.toString x@@ -87,7 +87,7 @@ -- | Used for commands which require a path within the database. -- If empty, the root path is used. newtype Path = Path ByteString- deriving (Eq, Show, MPDArg)+ deriving (Eq, Show, Ord, MPDArg) instance ToString Path where toString (Path x) = UTF8.toString x@@ -124,7 +124,7 @@ -- | A metadata value. newtype Value = Value ByteString- deriving (Eq, Show, MPDArg)+ deriving (Eq, Show, Ord, MPDArg) instance ToString Value where toString (Value x) = UTF8.toString x
src/Network/MPD/Core.hs view
@@ -26,11 +26,10 @@ import Network.MPD.Core.Error import Data.Char (isDigit)-import Control.Applicative (Applicative(..), (<$>), (<*)) import qualified Control.Exception as E import Control.Exception.Safe (catch, catchAny) import Control.Monad (ap, unless)-import Control.Monad.Error (ErrorT(..), MonadError(..))+import Control.Monad.Except (ExceptT(..),runExceptT, MonadError(..)) import Control.Monad.Reader (ReaderT(..), ask) import Control.Monad.State (StateT, MonadIO(..), modify, gets, evalStateT) import qualified Data.Foldable as F@@ -76,14 +75,14 @@ -- -- To use the error throwing\/catching capabilities: ----- > import Control.Monad.Error (throwError, catchError)+-- > import Control.Monad.Except (throwError, catchError) -- -- To run IO actions within the MPD monad: -- -- > import Control.Monad.Trans (liftIO) newtype MPD a =- MPD { runMPD :: ErrorT MPDError+ MPD { runMPD :: ExceptT MPDError (StateT MPDState (ReaderT (Host, Port) IO)) a } deriving (Functor, Monad, MonadIO, MonadError MPDError)@@ -113,7 +112,7 @@ -- | The most configurable API for running an MPD action. withMPDEx :: Host -> Port -> Password -> MPD a -> IO (Response a) withMPDEx host port pw x = withSocketsDo $- runReaderT (evalStateT (runErrorT . runMPD $ open >> (x <* close)) initState)+ runReaderT (evalStateT (runExceptT . runMPD $ open >> (x <* close)) initState) (host, port) where initState = MPDState Nothing pw (0, 0, 0)
src/Network/MPD/Core/Class.hs view
@@ -14,7 +14,7 @@ import Network.MPD.Core.Error (MPDError) -import Control.Monad.Error (MonadError)+import Control.Monad.Except (MonadError) type Password = String
src/Network/MPD/Core/Error.hs view
@@ -15,7 +15,6 @@ module Network.MPD.Core.Error where import qualified Control.Exception as E-import Control.Monad.Error (Error(..)) import Data.Typeable -- | The MPDError type is used to signal errors, both from the MPD and@@ -44,10 +43,6 @@ ] show (Custom s) = s show (ACK _ s) = s--instance Error MPDError where- noMsg = Custom "An error occurred"- strMsg = Custom -- | Represents various MPD errors (aka. ACKs). data ACKType = InvalidArgument -- ^ Invalid argument passed (ACK 2)
src/Network/MPD/Util.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, CPP #-}+{-# LANGUAGE OverloadedStrings #-} -- | Module : Network.MPD.Util -- Copyright : (c) Ben Sinclair 2005-2009, Joachim Fasting 2010 -- License : MIT (see LICENSE)@@ -15,13 +15,9 @@ import Control.Arrow -import Data.Time.Format (ParseTime, parseTime, FormatTime, formatTime)+import Data.Time.Format (ParseTime, parseTimeM, FormatTime, formatTime) -#if MIN_VERSION_time(1,5,0) import Data.Time.Format (defaultTimeLocale)-#else-import System.Locale (defaultTimeLocale)-#endif import qualified Prelude import Prelude hiding (break, take, drop, dropWhile, read)@@ -50,7 +46,7 @@ -- Parse date in iso 8601 format parseIso8601 :: (ParseTime t) => ByteString -> Maybe t-parseIso8601 = parseTime defaultTimeLocale iso8601Format . UTF8.toString+parseIso8601 = parseTimeM True defaultTimeLocale iso8601Format . UTF8.toString formatIso8601 :: FormatTime t => t -> String formatIso8601 = formatTime defaultTimeLocale iso8601Format
tests/ParserSpec.hs view
@@ -51,8 +51,6 @@ prop_parseOutputs ds = Right ds `shouldBe` (parseOutputs . map UTF8.fromString . lines . concatMap unparse) ds -deriving instance Ord Value- prop_parseSong :: Song -> Expectation prop_parseSong s = Right (sortTags s) `shouldBe` sortTags `fmap` (parseSong . toAssocList . map UTF8.fromString . lines . unparse) s where
tests/StringConn.hs view
@@ -13,7 +13,7 @@ import Control.Applicative import Prelude hiding (exp)-import Control.Monad.Error+import Control.Monad.Except import Control.Monad.Identity import Control.Monad.Reader import Control.Monad.State@@ -37,7 +37,7 @@ deriving (Show, Eq) newtype StringMPD a =- SMPD { runSMPD :: ErrorT MPDError+ SMPD { runSMPD :: ExceptT MPDError (StateT [(Expect, Response String)] (ReaderT Password Identity)) a } deriving (Functor, Applicative, Monad, MonadError MPDError)@@ -73,4 +73,4 @@ -> Password -- ^ A password to be supplied. -> StringMPD a -- ^ The MPD action to run. -> Response a-testMPDWithPassword pairs passwd m = runIdentity $ runReaderT (evalStateT (runErrorT $ runSMPD m) pairs) passwd+testMPDWithPassword pairs passwd m = runIdentity $ runReaderT (evalStateT (runExceptT $ runSMPD m) pairs) passwd