{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}

-- | Some helpers to parse documents with Text.XML.Cursor.

module Network.SOAP.Parsing.Cursor
    (
      -- * Extract single element
      readT, readC
      -- * Extract from multiple elements
    , Dict, readDict, dictBy
    ) where

import Network.SOAP (ResponseParser(CursorParser))

import Text.XML
import Text.XML.Cursor

import           Data.Text (Text)
import qualified Data.Text as T
import qualified Data.HashMap.Strict as HM
import           Data.Maybe (mapMaybe)

-- ** Single-element extraction.

-- | Grab node content by element name.
--
-- > pair cur = (readT "fst" cur, readT "snd" cur)
readT :: Text -> Cursor -> Text
readT n c = T.concat $ c $/ laxElement n &/ content
{-# INLINE readT #-}

-- | Extract a read-able type from a content of a node with given name.
--
-- > age = readC "age" :: Cursor -> Integer
readC :: (Read a) => Text -> Cursor -> a
readC n c = read . T.unpack $ readT n c
{-# INLINE readC #-}

-- ** Multi-element extraction.

-- | Very generic type to catch server reply when you don't care about types.
type Dict = HM.HashMap Text Text

-- | Apply an axis and extract a key-value from child elements.
--
-- > invokeWS … (CursorParser . readDict $ laxElement "WebScaleResponse" &/ laxElement "BigDataResult")
readDict :: Axis -> Cursor -> Dict
readDict a c = extract . head $ c $/ a
    where
        extract cur = HM.fromList . mapMaybe dict . map node $ cur $| child

        dict (NodeElement (Element (Name n _ _) _ [NodeContent cont])) = Just (n, cont)
        dict (NodeElement (Element (Name n _ _) _ []))              = Just (n, T.empty)
        dict _                                                      = Nothing

-- | Simple parser to grab a flat response by an element name.
--
-- > result <- invokeWS … (dictBy "BigDataResult")
-- > case HM.lookup "SuccessError" result of …
dictBy :: T.Text -> ResponseParser Dict
dictBy n = CursorParser . readDict $ anyElement &/ laxElement n