{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Yesod.Form.Types
(
Enctype (..)
, FormResult (..)
, FormMessage (..)
, Env
, FileEnv
, Ints (..)
, WForm
, MForm
, AForm (..)
, Field (..)
, FieldSettings (..)
, FieldView (..)
, FieldViewFunc
) where
import Control.Monad.Trans.RWS (RWST)
import Control.Monad.Trans.Writer (WriterT)
import Data.Text (Text)
import Data.Monoid (Monoid (..))
import Text.Blaze (Markup, ToMarkup (toMarkup), ToValue (toValue))
#define Html Markup
#define ToHtml ToMarkup
#define toHtml toMarkup
import Control.Applicative ((<$>), Alternative (..), Applicative (..))
import Control.Monad (liftM)
import Control.Monad.Trans.Class
import Data.String (IsString (..))
import Yesod.Core
import qualified Data.Map as Map
import Data.Semigroup (Semigroup, (<>))
import Data.Traversable
import Data.Foldable
data FormResult a = FormMissing
| FormFailure [Text]
| FormSuccess a
deriving (Show, Eq)
instance Functor FormResult where
fmap _ FormMissing = FormMissing
fmap _ (FormFailure errs) = FormFailure errs
fmap f (FormSuccess a) = FormSuccess $ f a
instance Control.Applicative.Applicative FormResult where
pure = FormSuccess
(FormSuccess f) <*> (FormSuccess g) = FormSuccess $ f g
(FormFailure x) <*> (FormFailure y) = FormFailure $ x ++ y
(FormFailure x) <*> _ = FormFailure x
_ <*> (FormFailure y) = FormFailure y
_ <*> _ = FormMissing
instance Data.Monoid.Monoid m => Monoid (FormResult m) where
mempty = pure mempty
mappend x y = mappend <$> x <*> y
instance Semigroup m => Semigroup (FormResult m) where
x <> y = (<>) Control.Applicative.<$> x <*> y
instance Data.Foldable.Foldable FormResult where
foldMap f r = case r of
FormSuccess a -> f a
FormFailure _errs -> mempty
FormMissing -> mempty
instance Data.Traversable.Traversable FormResult where
traverse f r = case r of
FormSuccess a -> fmap FormSuccess (f a)
FormFailure errs -> pure (FormFailure errs)
FormMissing -> pure FormMissing
instance Alternative FormResult where
empty = FormMissing
FormFailure e <|> _ = FormFailure e
_ <|> FormFailure e = FormFailure e
FormSuccess s <|> FormSuccess _ = FormSuccess s
FormMissing <|> result = result
result <|> FormMissing = result
data Enctype = UrlEncoded | Multipart
deriving (Eq, Enum, Bounded)
instance ToHtml Enctype where
toHtml UrlEncoded = "application/x-www-form-urlencoded"
toHtml Multipart = "multipart/form-data"
instance ToValue Enctype where
toValue UrlEncoded = "application/x-www-form-urlencoded"
toValue Multipart = "multipart/form-data"
instance Monoid Enctype where
mempty = UrlEncoded
#if !(MIN_VERSION_base(4,11,0))
mappend = (<>)
#endif
instance Semigroup Enctype where
UrlEncoded <> UrlEncoded = UrlEncoded
_ <> _ = Multipart
data Ints = IntCons Int Ints | IntSingle Int
instance Show Ints where
show (IntSingle i) = show i
show (IntCons i is) = show i ++ ('-' : show is)
type Env = Map.Map Text [Text]
type FileEnv = Map.Map Text [FileInfo]
type WForm m a = MForm (WriterT [FieldView (HandlerSite m)] m) a
type MForm m a = RWST
(Maybe (Env, FileEnv), HandlerSite m, [Lang])
Enctype
Ints
m
a
newtype AForm m a = AForm
{ unAForm :: (HandlerSite m, [Text])
-> Maybe (Env, FileEnv)
-> Ints
-> m (FormResult a, [FieldView (HandlerSite m)] -> [FieldView (HandlerSite m)], Ints, Enctype)
}
instance Monad m => Functor (AForm m) where
fmap f (AForm a) =
AForm $ \x y z -> liftM go $ a x y z
where
go (w, x, y, z) = (fmap f w, x, y, z)
instance Monad m => Applicative (AForm m) where
pure x = AForm $ const $ const $ \ints -> return (FormSuccess x, id, ints, mempty)
(AForm f) <*> (AForm g) = AForm $ \mr env ints -> do
(a, b, ints', c) <- f mr env ints
(x, y, ints'', z) <- g mr env ints'
return (a <*> x, b . y, ints'', c `mappend` z)
instance (Monad m, Monoid a) => Monoid (AForm m a) where
mempty = pure mempty
mappend a b = mappend <$> a <*> b
instance (Monad m, Semigroup a) => Semigroup (AForm m a) where
a <> b = (<>) <$> a <*> b
instance MonadTrans AForm where
lift f = AForm $ \_ _ ints -> do
x <- f
return (FormSuccess x, id, ints, mempty)
data FieldSettings master = FieldSettings
{ fsLabel :: SomeMessage master
, fsTooltip :: Maybe (SomeMessage master)
, fsId :: Maybe Text
, fsName :: Maybe Text
, fsAttrs :: [(Text, Text)]
}
instance IsString (FieldSettings a) where
fromString s = FieldSettings (fromString s) Nothing Nothing Nothing []
data FieldView site = FieldView
{ fvLabel :: Html
, fvTooltip :: Maybe Html
, fvId :: Text
, fvInput :: WidgetFor site ()
, fvErrors :: Maybe Html
, fvRequired :: Bool
}
type FieldViewFunc m a
= Text
-> Text
-> [(Text, Text)]
-> Either Text a
-> Bool
-> WidgetFor (HandlerSite m) ()
data Field m a = Field
{ fieldParse :: [Text] -> [FileInfo] -> m (Either (SomeMessage (HandlerSite m)) (Maybe a))
, fieldView :: FieldViewFunc m a
, fieldEnctype :: Enctype
}
data FormMessage = MsgInvalidInteger Text
| MsgInvalidNumber Text
| MsgInvalidEntry Text
| MsgInvalidUrl Text
| MsgInvalidEmail Text
| MsgInvalidTimeFormat
| MsgInvalidHour Text
| MsgInvalidMinute Text
| MsgInvalidSecond Text
| MsgInvalidDay
| MsgCsrfWarning
| MsgValueRequired
| MsgInputNotFound Text
| MsgSelectNone
| MsgInvalidBool Text
| MsgBoolYes
| MsgBoolNo
| MsgDelete
deriving (Show, Eq, Read)