Input.hs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. {-# LANGUAGE OverloadedStrings #-}
  2. {-# LANGUAGE FlexibleContexts #-}
  3. -- | Provides for getting input from either GET or POST params without
  4. -- generating HTML forms. For more information, see:
  5. -- <http://www.yesodweb.com/book/forms#forms_kinds_of_forms>.
  6. module Yesod.Form.Input
  7. ( FormInput (..)
  8. , runInputGet
  9. , runInputGetResult
  10. , runInputPost
  11. , runInputPostResult
  12. , ireq
  13. , iopt
  14. ) where
  15. import Yesod.Form.Types
  16. import Data.Text (Text)
  17. import Control.Applicative (Applicative (..))
  18. import Yesod.Core
  19. import Control.Monad (liftM, (<=<))
  20. import qualified Data.Map as Map
  21. import Data.Maybe (fromMaybe)
  22. import Control.Arrow ((***))
  23. type DText = [Text] -> [Text]
  24. -- | Type for a form which parses a value of type @a@ with the base monad @m@
  25. -- (usually your @Handler@). Can can compose this using its @Applicative@ instance.
  26. newtype FormInput m a = FormInput { unFormInput :: HandlerSite m -> [Text] -> Env -> FileEnv -> m (Either DText a) }
  27. instance Monad m => Functor (FormInput m) where
  28. fmap a (FormInput f) = FormInput $ \c d e e' -> liftM (either Left (Right . a)) $ f c d e e'
  29. instance Monad m => Applicative (FormInput m) where
  30. pure = FormInput . const . const . const . const . return . Right
  31. (FormInput f) <*> (FormInput x) = FormInput $ \c d e e' -> do
  32. res1 <- f c d e e'
  33. res2 <- x c d e e'
  34. return $ case (res1, res2) of
  35. (Left a, Left b) -> Left $ a . b
  36. (Left a, _) -> Left a
  37. (_, Left b) -> Left b
  38. (Right a, Right b) -> Right $ a b
  39. -- | Promote a @Field@ into a @FormInput@, requiring that the value be present
  40. -- and valid.
  41. ireq :: (Monad m, RenderMessage (HandlerSite m) FormMessage)
  42. => Field m a
  43. -> Text -- ^ name of the field
  44. -> FormInput m a
  45. ireq field name = FormInput $ \m l env fenv -> do
  46. let filteredEnv = fromMaybe [] $ Map.lookup name env
  47. filteredFEnv = fromMaybe [] $ Map.lookup name fenv
  48. emx <- fieldParse field filteredEnv filteredFEnv
  49. return $ case emx of
  50. Left (SomeMessage e) -> Left $ (:) $ renderMessage m l e
  51. Right Nothing -> Left $ (:) $ renderMessage m l $ MsgInputNotFound name
  52. Right (Just a) -> Right a
  53. -- | Promote a @Field@ into a @FormInput@, with its presence being optional. If
  54. -- the value is present but does not parse correctly, the form will still fail.
  55. iopt :: Monad m => Field m a -> Text -> FormInput m (Maybe a)
  56. iopt field name = FormInput $ \m l env fenv -> do
  57. let filteredEnv = fromMaybe [] $ Map.lookup name env
  58. filteredFEnv = fromMaybe [] $ Map.lookup name fenv
  59. emx <- fieldParse field filteredEnv filteredFEnv
  60. return $ case emx of
  61. Left (SomeMessage e) -> Left $ (:) $ renderMessage m l e
  62. Right x -> Right x
  63. -- | Run a @FormInput@ on the GET parameters (i.e., query string). If parsing
  64. -- fails, calls 'invalidArgs'.
  65. runInputGet :: MonadHandler m => FormInput m a -> m a
  66. runInputGet = either invalidArgs return <=< runInputGetHelper
  67. -- | Run a @FormInput@ on the GET parameters (i.e., query string). Does /not/
  68. -- throw exceptions on failure.
  69. --
  70. -- Since 1.4.1
  71. runInputGetResult :: MonadHandler m => FormInput m a -> m (FormResult a)
  72. runInputGetResult = fmap (either FormFailure FormSuccess) . runInputGetHelper
  73. runInputGetHelper :: MonadHandler m => FormInput m a -> m (Either [Text] a)
  74. runInputGetHelper (FormInput f) = do
  75. env <- liftM (toMap . reqGetParams) getRequest
  76. m <- getYesod
  77. l <- languages
  78. emx <- f m l env Map.empty
  79. return $ either (Left . ($ [])) Right emx
  80. toMap :: [(Text, a)] -> Map.Map Text [a]
  81. toMap = Map.unionsWith (++) . map (\(x, y) -> Map.singleton x [y])
  82. -- | Run a @FormInput@ on the POST parameters (i.e., request body). If parsing
  83. -- fails, calls 'invalidArgs'.
  84. runInputPost :: MonadHandler m => FormInput m a -> m a
  85. runInputPost = either invalidArgs return <=< runInputPostHelper
  86. -- | Run a @FormInput@ on the POST parameters (i.e., request body). Does /not/
  87. -- throw exceptions on failure.
  88. runInputPostResult :: MonadHandler m => FormInput m a -> m (FormResult a)
  89. runInputPostResult = fmap (either FormFailure FormSuccess) . runInputPostHelper
  90. runInputPostHelper :: MonadHandler m => FormInput m a -> m (Either [Text] a)
  91. runInputPostHelper (FormInput f) = do
  92. (env, fenv) <- liftM (toMap *** toMap) runRequestBody
  93. m <- getYesod
  94. l <- languages
  95. fmap (either (Left . ($ [])) Right) $ f m l env fenv