module AWS.Lib.Parser
( RequestId
, getT
, getT_
, element
, elementM
, listConduit
, listConsumer
, isBeginTagName
, awaitWhile
, sinkResponse
, sinkResponseMetadata
, sinkError
, sinkEventBeginDocument
, members
, text
, FromText(..)
) where
import Data.XML.Types (Event(..), Name(..))
import Data.Text (Text)
import Data.ByteString (ByteString)
import Data.Conduit
import qualified Data.Conduit.List as CL
import qualified Text.XML.Stream.Parse as XML
import Control.Applicative
import Data.Monoid
import Control.Monad.Trans.Class (lift)
import AWS.Class
import AWS.Lib.FromText
type RequestId = Text
text :: MonadThrow m => GLSink Event m Text
text = XML.content
listConduit :: MonadThrow m
=> Text
-> GLSink Event m o
-> GLConduit Event m o
listConduit name p =
awaitWhile isTag >>= maybe (return ()) (\e -> do
leftover e
if isBeginTagName name e
then do
element name $ p >>= yield
listConduit name p
else return ()
)
listConsumer :: MonadThrow m
=> Text
-> GLSink Event m o
-> GLSink Event m [o]
listConsumer name p = listConduit name p >+> CL.consume
isTag :: Event -> Bool
isTag (EventBeginElement _ _) =True
isTag (EventEndElement _) =True
isTag _ = False
sinkDropWhile :: Monad m => (i -> Bool) -> GLSink i m ()
sinkDropWhile f = await >>= maybe (return ()) g
where
g i | f i = sinkDropWhile f
| otherwise = leftover i >> return ()
isBeginTagName :: Text -> Event -> Bool
isBeginTagName name (EventBeginElement n _)
| nameLocalName n == name = True
| otherwise = False
isBeginTagName _ _ = False
awaitWhile :: Monad m
=> (i -> Bool)
-> Pipe l i o u m (Maybe i)
awaitWhile f = await >>= g
where
g Nothing = return Nothing
g (Just a)
| f a = return $ Just a
| otherwise = awaitWhile f
getT :: (MonadThrow m, FromText a)
=> Text
-> Pipe Event Event o u m a
getT name = elementM name text >>= lift . fromMaybeText name
getT_ :: forall m o u . MonadThrow m
=> Text
-> Pipe Event Event o u m ()
getT_ name = () <$ (getT name :: Pipe Event Event o u m Text)
elementM :: forall o u m a . MonadThrow m
=> Text
-> Pipe Event Event o u m a
-> Pipe Event Event o u m (Maybe a)
elementM name inner = do
sinkDropWhile $ not . isTag
XML.tagPredicate g (return ()) $ const inner
where
g n = (nameLocalName n == name)
element :: forall o u m a . MonadThrow m
=> Text
-> Pipe Event Event o u m a
-> Pipe Event Event o u m a
element name inner = elementM name inner >>=
maybe (lift $ monadThrow $ ResponseParseError name) return
sinkResponse
:: MonadThrow m
=> Text
-> GLSink Event m a
-> GLSink Event m (a, RequestId)
sinkResponse action sink = do
sinkEventBeginDocument
element (action <> "Response") $ (,)
<$> element (action <> "Result") sink
<*> sinkResponseMetadata
sinkResponseMetadata
:: MonadThrow m
=> GLSink Event m Text
sinkResponseMetadata =
element "ResponseMetadata" $
getT "RequestId"
sinkEventBeginDocument
:: MonadThrow m
=> GLSink Event m ()
sinkEventBeginDocument = do
me <- await
case me of
Nothing -> return ()
Just EventBeginDocument -> return ()
Just _ -> fail $ "unexpected: " <> show me
sinkError :: MonadThrow m => ByteString -> Int -> GLSink Event m a
sinkError action status = element "ErrorResponse" $ do
(c,m) <- element "Error" $ (,)
<$> (getT_ "Type" *> getT "Code")
<*> getT "Message"
rid <- getT "RequestId"
lift $ monadThrow $ ClientError action status c m rid
members :: MonadThrow m
=> Text
-> GLSink Event m a
-> GLSink Event m [a]
members name f = element name $ listConsumer "member" f