{-# LANGUAGE TypeOperators #-} module Text.Formlets ( module Text.Formlets.Form , input , password , inputF , passwordF,ensure , FailingForm, validate, runFormState, inputIntegerF , check, liftForm ) where import Text.Formlets.Form import Control.Applicative import Control.Applicative.Compose import Control.Applicative.Error import Control.Applicative.State import Text.XHtml.Strict ((!)) import qualified Text.XHtml.Strict as X -- | Apply a predicate to a value and return Success or Failure as appropriate ensure :: Show a => (a -> Bool) -- ^ The predicate -> String -- ^ The error message, in case the predicate fails -> a -- ^ The value -> Failing a ensure p msg x | p x = Success x | otherwise = Failure [msg] -- | Helper function for genereting input components based. input' :: (String -> String -> X.Html) -> Maybe String -> Form String input' i defaultValue = Form $ \env -> mkInput env <$> freshName where mkInput :: Env -> String -> (Collector String, Xml) mkInput env name = (`queryParam` name, i name (value name env)) value name env = maybe (maybe "" id defaultValue) id (lookup name env) -- | A form whose output may fail type FailingForm a = (Form :+: Failing) a runFormState :: Env -- ^ A previously filled environment (may be empty) -> FailingForm a -- ^ The form -> FormState -- ^ Initial form state -> ((Collector (Failing a), Xml), FormState) runFormState e f s = (runState (deform (decompose f) e) s) -- | Lifts a function on a Form to a function on a composed form. liftForm :: (Form (f a) -> Form (f a)) -> (Form :+: f) a -> (Form :+: f) a liftForm f = Compose . f . decompose -- | Lift a form component to a failing form component validate :: Form a -> FailingForm a validate f = Compose $ pure Success <*> f -- | Add additional validation to an already validated component check :: FailingForm a -> (a -> Failing b) -> FailingForm b check form f = decompose form `chk` checker f where chk :: Form a -> (a -> Failing b) -> FailingForm b chk form validator = Compose $ pure validator <*> form checker :: (a -> Failing b) -> (Failing a -> Failing b) checker f (Failure x) = Failure x checker f (Success x) = f x -- | Component: an input field with an optional value input :: Maybe String -> Form String input = input' (\n v -> X.textfield n ! [X.value v]) -- | Component: a password field with an optional value password :: Maybe String -> Form String password = input' (\n v -> X.password n ! [X.value v]) -- | A trivially validated input component inputF :: Maybe String -> FailingForm String inputF = validate . input -- | A trivially validated password component passwordF :: Maybe String -> FailingForm String passwordF = validate . password -- | A validated integer component inputIntegerF :: Maybe Integer -> FailingForm Integer inputIntegerF x = validate (input $ fmap show x) `check` asInteger