{-# LANGUAGE ScopedTypeVariables #-}

module Data.Validator.Failure where

import qualified Data.Aeson.Pointer as P

import           Import

-- For GHCs before 7.10:
import           Prelude            hiding (concat, sequence)

-- | Validators shouldn't know more about the schema they're going to
-- be used with than necessary. If a validator throws errors using the
-- error sum type of a particular schema, then it can't be used with
-- other schemas later that have different error sum types (at least not
-- without writing partial functions).
--
-- Thus validators that can only fail in one way return 'FailureInfo's.
-- Validators that can fail in multiple ways return 'ValidationFailure's
-- along with an custom error sum type for that particular validator.
--
-- It's the job of a schema's validate function to unify the errors produced
-- by the validators it uses into a single error sum type for that schema.
-- The schema's validate function will return a 'ValidationFailure' with
-- that sum type as its type argument.
data Failure err = Invalid
  { _invalidValidatorsCalled :: !err
  , _invalidFinalValidator   :: !Value
  , _invalidOffendingData    :: !P.Pointer
  } deriving (Eq, Show)

setFailure :: b -> Failure a -> Failure b
setFailure e (Invalid _ a b) = Invalid e a b

modFailure :: (a -> b) -> Failure a -> Failure b
modFailure f v@(Invalid a _ _) = v { _invalidValidatorsCalled = f a }

addToPath :: P.Token -> Failure a -> Failure a
addToPath tok v@(Invalid _ _ a) = v { _invalidOffendingData = addToken a }
  where
    addToken :: P.Pointer -> P.Pointer
    addToken (P.Pointer ts) = P.Pointer (ts <> pure tok)