{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} 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 -- ^ Action -> GLSink Event m a -> GLSink Event m (a, RequestId) sinkResponse action sink = do sinkEventBeginDocument element (action <> "Response") $ (,) <$> element (action <> "Result") sink -- XXX: parse Marker <*> 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