haste-gapi-0.1.0.0: Haste/GAPI/Result.hs
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Haste.GAPI.Result
Description : Managing Results within the Google API
Copyright : (c) Jonathan Skårstedt, 2016
License : MIT
Maintainer : jonathan.skarstedt@gmail.com
Stability : experimental
Portability : Haste
This module contains functions to work on results from requests to the Google
API.
-}
module Haste.GAPI.Result (
Result(),
Raw(..),
get, has, hasAll,
lookupVal, lookupResult,
val, valOf, valMaybe,
parp, children,
toResult
) where
import Haste
import qualified Haste.Foreign as F
import qualified Haste.JSString as J
-- GHC 7.8 compatibility
import Data.Functor ((<$>))
import Haste.GAPI.Request.Types
import Haste.GAPI.Request.RequestM
-- | Result mapping. The Result takes a parameter to act as a phantom type
-- on which kind of object you're currently working on.
newtype Result a = Result {rawR :: F.JSAny}
-- | Transforms a JSAny to a Result
toResult :: JSAny -> Result a
toResult = Result
-- | Default result parameter.
data Raw
-- | Fetches a data field from a GAPI Result
get :: Result a -> JSString -> RequestM (Result Raw)
get (Result r) key = liftIO $ do
h <- F.has r key
if h then Result <$> F.get r key
else fail $ "Child '" ++ J.unpack key ++ "' was not found."
-- | Looks up a value by a deep index
-- Example:
--
-- @
-- rLookup res "a.b.c"
-- @
--
-- This will look up`(a.b.c)` where `a` is a field of
-- `res`, `b` is a field of `a`, and `c` is a field of `b`.
lookupVal :: F.FromAny any => Result a -> JSString -> RequestM (Maybe any)
lookupVal res keys = liftIO $ do
v <- F.lookupAny (rawR res) keys
case v of
Just a -> Just <$> F.fromAny a
Nothing -> return Nothing
-- | Fetches a result from a deep index
lookupResult :: Result a -> JSString -> RequestM (Maybe (Result Raw))
lookupResult res keys
= liftIO $ fmap Result <$> F.lookupAny (rawR res) keys
-- | Checks if a Result has a certain field
has :: Result a -> JSString -> RequestM Bool
has (Result res) key = liftIO $ F.has res key
-- | Checks if a Result has several different fields. Will return true only
-- if all these fields exists.
hasAll :: Result a -> [JSString] -> RequestM Bool
hasAll res keys = and <$> mapM (has res) keys
-- | Fetches a value from a field, in contrast to a result.
-- Field must contain a value compatible with fromAny
-- Terminates with an error if the field cannot be found.
val :: F.FromAny any => Result a -> JSString -> RequestM any
val r key = liftIO $ F.get (rawR r) key >>= F.fromAny
-- | Fetches the value of the result directly.
valOf :: F.FromAny any => Result a -> RequestM any
valOf = liftIO . F.fromAny . rawR
-- | Same as `val`, but will return Nothing if there is no such field.
valMaybe :: F.FromAny any => Result a -> JSString -> RequestM (Maybe any)
valMaybe (Result res) key = liftIO $ do
exists <- F.has res key
if exists then Just <$> (F.get res key >>= F.fromAny)
else return Nothing
-- | Takes a result and a list of keys and maps these values to Params.
parp :: Result a -> [JSString] -> RequestM (Maybe Params)
parp r keys = do
exists <- hasAll r keys
if not exists
then return Nothing
else do vals <- mapM (val r) keys
return . Just . Params $ zip keys vals
-- | Transforms a JSAny representing a list into a list of its children
children' :: JSAny -> IO [JSAny]
children' = F.fromAny
-- | Checks if a fromAny is an array
isArray :: JSAny -> IO Bool
isArray = F.ffi "(function(a) {\
\return a.prop \
\&& a.prop.constructor === Array;\
\})"
-- | Transforms a result which represents an array into a list of child
-- elements
children :: Result a -> RequestM (Maybe [Result b])
children (Result res) = do
arr <- liftIO $ isArray res
if arr
then do
childs <- liftIO $ children' res
return . Just $ map Result childs
else return Nothing