module Alfred.Query
( jsonQuery
, jsonQuery'
, xmlQuery
, xmlQueryLazy
, escapeString
, escapeText
, Query
, transformQuery
, Query'
) where
import Network.HTTP
import Network.BufferType
import Network.URI hiding (escapeString)
import Data.Aeson
import qualified Data.Text as T
import Data.Text (Text)
import System.IO.Error
import Data.ByteString
import Text.XML.Expat.Tree
type Query a = Query' Text a
type Query' q a = q -> IO (Either Text a)
jsonQuery :: FromJSON a => Text -> Query a
jsonQuery = jsonQuery' id
jsonQuery' :: FromJSON a => (ByteString -> ByteString) -> Text -> Query a
jsonQuery' convert = genericQuery mkJSONRequest result
where result res = case eitherDecodeStrict (convert $ rspBody res) of
Left msg -> return $ Left $ T.concat
["JSON decoding error: ", T.pack msg, "\n", T.pack $ show $ rspBody res]
Right res -> return (Right res)
mkJSONRequest :: BufferType ty => URI -> Request ty
mkJSONRequest url = setHeaders (mkRequest GET url) jsonHeaders
where jsonHeaders :: [Header]
jsonHeaders = [mkHeader HdrContentType "application/json; charset=utf-8"]
xmlQuery :: (GenericXMLString a, GenericXMLString b) => Text -> Query (Node a b)
xmlQuery = genericQuery mkXMLRequest result
where result res = case parse' defaultParseOptions (rspBody res) of
Left msg -> return $ Left $ T.concat
["XML decoding error: ", T.pack $ show msg ,"\n", T.pack $ show (rspBody res)]
Right tree -> return (Right tree)
xmlQueryLazy :: (GenericXMLString a, GenericXMLString b) => Text -> Query (Node a b)
xmlQueryLazy = genericQuery mkXMLRequest result
where result res = let (tree, _) = parse defaultParseOptions (rspBody res)
in return (Right tree)
genericQuery :: HStream ty => (URI -> Request ty)
-> (Response ty -> IO (Either Text b))
-> Text -> Query b
genericQuery mkRequest result base query = let urlText = T.concat [base, escapeText query] in
case (parseURI $ T.unpack urlText) of
Nothing -> return $ Left $ T.concat ["illformed url: ", urlText]
Just url -> catchIOError execute (return . Left . T.pack . show)
where execute = do
res <- simpleHTTP (mkRequest url)
case res of
Left err -> return $ Left $ T.pack (show err)
Right res -> result res
mkXMLRequest :: BufferType ty => URI -> Request ty
mkXMLRequest url = setHeaders (mkRequest GET url) xmlHeaders
where xmlHeaders :: [Header]
xmlHeaders = [mkHeader HdrContentType "application/xml"]
transformQuery :: (a -> b) -> Query' q a -> Query' q b
transformQuery f = fmap (fmap (fmap f))
escapeString :: String -> String
escapeString = escapeURIString isUnescapedInURI
escapeText :: Text -> Text
escapeText = T.pack . escapeString . T.unpack