{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ViewPatterns #-}

-- | Validating indexed formlet with auto-generated input names.

module Descriptive.Formlet
  (-- * Combinators
   indexed
  ,FormletState(..)
  -- * Description
  ,Formlet(..))
  where

import           Descriptive

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

-- | Description of a formlet.
data Formlet
  = Index !Integer
  | Constrained !Text
  deriving (Int -> Formlet -> ShowS
[Formlet] -> ShowS
Formlet -> String
(Int -> Formlet -> ShowS)
-> (Formlet -> String) -> ([Formlet] -> ShowS) -> Show Formlet
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Formlet] -> ShowS
$cshowList :: [Formlet] -> ShowS
show :: Formlet -> String
$cshow :: Formlet -> String
showsPrec :: Int -> Formlet -> ShowS
$cshowsPrec :: Int -> Formlet -> ShowS
Show,Formlet -> Formlet -> Bool
(Formlet -> Formlet -> Bool)
-> (Formlet -> Formlet -> Bool) -> Eq Formlet
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Formlet -> Formlet -> Bool
$c/= :: Formlet -> Formlet -> Bool
== :: Formlet -> Formlet -> Bool
$c== :: Formlet -> Formlet -> Bool
Eq)

-- | State used when running a formlet.
data FormletState =
  FormletState {FormletState -> Map Integer Text
formletMap :: (Map Integer Text)
               ,FormletState -> Integer
formletIndex :: !Integer}
  deriving (Int -> FormletState -> ShowS
[FormletState] -> ShowS
FormletState -> String
(Int -> FormletState -> ShowS)
-> (FormletState -> String)
-> ([FormletState] -> ShowS)
-> Show FormletState
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FormletState] -> ShowS
$cshowList :: [FormletState] -> ShowS
show :: FormletState -> String
$cshow :: FormletState -> String
showsPrec :: Int -> FormletState -> ShowS
$cshowsPrec :: Int -> FormletState -> ShowS
Show,FormletState -> FormletState -> Bool
(FormletState -> FormletState -> Bool)
-> (FormletState -> FormletState -> Bool) -> Eq FormletState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FormletState -> FormletState -> Bool
$c/= :: FormletState -> FormletState -> Bool
== :: FormletState -> FormletState -> Bool
$c== :: FormletState -> FormletState -> Bool
Eq)

-- | Consume any character.
indexed :: Monad m => Consumer FormletState Formlet m Text
indexed :: Consumer FormletState Formlet m Text
indexed =
  StateT FormletState m (Description Formlet)
-> StateT FormletState m (Result (Description Formlet) Text)
-> Consumer FormletState Formlet m Text
forall s (m :: * -> *) d a.
StateT s m (Description d)
-> StateT s m (Result (Description d) a) -> Consumer s d m a
consumer (do Integer
i <- StateT FormletState m Integer
forall (m :: * -> *). MonadState FormletState m => m Integer
nextIndex
               Description Formlet -> StateT FormletState m (Description Formlet)
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Description Formlet
d Integer
i))
           (do Integer
i <- StateT FormletState m Integer
forall (m :: * -> *). MonadState FormletState m => m Integer
nextIndex
               FormletState
s <- StateT FormletState m FormletState
forall s (m :: * -> *). MonadState s m => m s
get
               Result (Description Formlet) Text
-> StateT FormletState m (Result (Description Formlet) Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (case Integer -> Map Integer Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup Integer
i (FormletState -> Map Integer Text
formletMap FormletState
s) of
                         Nothing -> Description Formlet -> Result (Description Formlet) Text
forall e a. e -> Result e a
Failed (Integer -> Description Formlet
d Integer
i)
                         Just a :: Text
a -> Text -> Result (Description Formlet) Text
forall e a. a -> Result e a
Succeeded Text
a))
  where d :: Integer -> Description Formlet
d = Formlet -> Description Formlet
forall a. a -> Description a
Unit (Formlet -> Description Formlet)
-> (Integer -> Formlet) -> Integer -> Description Formlet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Formlet
Index
        nextIndex :: MonadState FormletState m => m Integer
        nextIndex :: m Integer
nextIndex =
          do Integer
i <- (FormletState -> Integer) -> m Integer
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets FormletState -> Integer
formletIndex
             (FormletState -> FormletState) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (\s :: FormletState
s ->
                       FormletState
s {formletIndex :: Integer
formletIndex = FormletState -> Integer
formletIndex FormletState
s Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ 1})
             Integer -> m Integer
forall (m :: * -> *) a. Monad m => a -> m a
return Integer
i