{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleContexts #-}

-- | Validating form with named inputs.

module Descriptive.Form
  (-- * Combinators
   input
  ,validate
  -- * Description
  ,Form (..)
  )
  where

import           Descriptive

import           Control.Arrow
import           Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import           Data.Text (Text)

-- | Form descriptor.
data Form
  = Input !Text
  | Constraint !Text
  deriving (Show)

-- | Consume any input value.
input :: Text -> Consumer (Map Text Text) Form Text
input name =
  consumer (d,)
           (\s ->
              (case M.lookup name s of
                 Nothing -> Left d
                 Just a -> Right a
              ,s))
  where d = Unit (Input name)

-- | Validate a form input with a description of what's required.
validate :: Text -- ^ Description of what it expects.
         -> (a -> Maybe b) -- ^ Attempt to parse the value.
         -> Consumer (Map Text Text) Form a -- ^ Consumer to add validation to.
         -> Consumer (Map Text Text) Form b
validate d' check =
  wrap (\s d -> redescribe (d s))
       (\s d p ->
          case p s of
            (Left e,s') -> (Left e,s')
            (Right a,s') ->
              case check a of
                Nothing ->
                  (Left (fst (redescribe (d s))),s')
                Just a' -> (Right a',s'))
  where redescribe = first (Wrap (Constraint d'))