content: {-# LANGUAGE OverloadedStrings #-}
  module Data.GraphQL.Error (
    parseError,
    CollectErrsT,
    addErr,
    addErrMsg,
    runCollectErrs,
    joinErrs,
    errWrap
    ) where
  
  import qualified Data.Aeson as Aeson
  import Data.Text (Text, pack)
  
  import Control.Arrow ((&&&))
  
  -- | Wraps a parse error into a list of errors.
  parseError :: Applicative f => String -> f Aeson.Value
  parseError s =
    pure $ Aeson.object [("errors", Aeson.toJSON [makeErrorMsg $ pack s])]
  
  -- | A wrapper for an 'Applicative' to pass error messages around.
  type CollectErrsT f a = f (a,[Aeson.Value])
  
  -- | Takes a (wrapped) list (foldable functor) of values and errors,
  --   joins the values into a list and concatenates the errors.
  joinErrs
    :: (Functor m, Functor f, Foldable f)
    => m (f (a,[Aeson.Value])) -> CollectErrsT m (f a)
  joinErrs = fmap $ fmap fst &&& concatMap snd
  
  -- | Wraps the given 'Applicative' to handle errors
  errWrap :: Functor f => f a -> f (a, [Aeson.Value])
  errWrap = fmap (flip (,) [])
  
  -- | Adds an error to the list of errors.
  addErr :: Functor f => Aeson.Value -> CollectErrsT f a -> CollectErrsT f a
  addErr v = (fmap . fmap) (v :)
  
  makeErrorMsg :: Text -> Aeson.Value
  makeErrorMsg s = Aeson.object [("message",Aeson.toJSON s)]
  
  -- | Convenience function for just wrapping an error message.
  addErrMsg :: Functor f => Text -> CollectErrsT f a -> CollectErrsT f a
  addErrMsg = addErr . makeErrorMsg
  
  -- | Runs the given query, but collects the errors into an error
  --   list which is then sent back with the data.
  runCollectErrs :: Functor f => CollectErrsT f Aeson.Value -> f Aeson.Value
  runCollectErrs = fmap finalD
    where
      finalD (dat,errs) =
          Aeson.object
        $ if null errs
             then [("data",dat)]
             else [("data",dat),("errors",Aeson.toJSON $ reverse errs)]
  