{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
-- | All documentation examples assume the following setup:
--
-- > :set -XOverloadedStrings
-- > import Data.Attoparsec.ByteString
-- > import Data.XML.Parser.High
module Data.XML.Parser.High.AttrParser
( AttrParser(..)
, anyAttr
, noAttr
, attrValue
, hasAttr
) where
import Control.Applicative
import Control.Arrow
import Control.Monad
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Text (Text)
import Data.XML.Parser.Low.Name
-- $setup
-- >>> :set -XOverloadedStrings
-- >>> import Data.Attoparsec.ByteString
-- >>> import Data.XML.Parser.High
-- | How to parse tag attributes.
newtype AttrParser a = AttrParser { runAttrParser :: Map QName Text -> Maybe a }
deriving instance Functor AttrParser
deriving via (WrappedArrow (Kleisli Maybe) (Map QName Text)) instance Applicative AttrParser
-- | Can be combined with @\<|\>@
deriving via (WrappedArrow (Kleisli Maybe) (Map QName Text)) instance Alternative AttrParser
-- | Can be combined with @>>=@. Attributes map is forwarded without change.
instance Monad AttrParser where
(AttrParser f) >>= g = AttrParser $ \attributes -> do
a <- f attributes
let AttrParser g' = g a
g' attributes
-- | Parse any set of attributes.
--
-- >>> parseOnly (runTokenParser $ tag' anyName anyAttr noContent) ""
-- Right ()
-- >>> parseOnly (runTokenParser $ tag' anyName anyAttr noContent) ""
-- Right ()
anyAttr :: AttrParser ()
anyAttr = pure ()
-- | Assert that no attributes exist.
--
-- >>> parseOnly (runTokenParser $ tag' anyName noAttr noContent) ""
-- Right ()
-- >>> parseOnly (runTokenParser $ tag' anyName noAttr noContent) ""
-- Left ...
noAttr :: AttrParser ()
noAttr = AttrParser $ \attributes -> if null attributes then Just () else Nothing
-- | Parse attribute by name, and return its value.
--
-- >>> parseOnly (runTokenParser $ tag' anyName (attrValue "foo") noContent) ""
-- Left ...
-- >>> parseOnly (runTokenParser $ tag' anyName (attrValue "foo") noContent) ""
-- Right ()
attrValue :: QName -> AttrParser Text
attrValue name = AttrParser $ Map.lookup name
-- | Assert that an attribute exists, with given name and value.
--
-- >>> parseOnly (runTokenParser $ tag' anyName (hasAttr "foo" "bar") noContent) ""
-- Left ...
-- >>> parseOnly (runTokenParser $ tag' anyName (hasAttr "foo" "bar") noContent) ""
-- Left ...
-- >>> parseOnly (runTokenParser $ tag' anyName (hasAttr "foo" "bar") noContent) ""
-- Right ()
hasAttr :: QName -> Text -> AttrParser ()
hasAttr name value = attrValue name >>= \value' -> guard (value == value')