{-# LANGUAGE Rank2Types #-}
module Summoner.Tui.Validation
( ctrlD
, summonFormValidation
, formErrorMessages
) where
import Brick.Forms (formState, invalidFields, setFieldValid, setFormFocus)
import Lens.Micro (Lens', (.~), (^.))
import Summoner.Tui.Form (KitForm, SummonForm (..), getCurrentFocus, mkForm)
import Summoner.Tui.Kit
import qualified Data.Text as T
ctrlD :: KitForm e -> KitForm e
ctrlD =
clearField "" UserFullName (user . fullName)
. clearField "" UserEmail (user . email)
. clearField "" ProjectName (project . repo)
. clearField "" ProjectDesc (project . desc)
. clearField "" ProjectCat (project . category)
. clearField "" CustomPreludeName (projectMeta . preludeName)
. clearField "" CustomPreludeModule (projectMeta . preludeModule)
. clearField [] Ghcs (projectMeta . ghcs)
. clearField "" UserOwner (user . owner)
where
clearField :: a -> SummonForm -> Lens' SummonKit a -> KitForm e -> KitForm e
clearField nil formField fieldLens f =
if getCurrentFocus f == Just formField
then setFormFocus formField $ mkForm $ formState f & fieldLens .~ nil
else f
summonFormValidation :: forall e . [FilePath] -> KitForm e -> KitForm e
summonFormValidation dirs kitForm = foldr setValidation kitForm universe
where
kit :: SummonKit
kit = formState kitForm
wrongFields :: [SummonForm]
wrongFields = case validateKit dirs kit of
Success _ -> []
Failure errors -> concatMap (toList . errorToInvalidFields) errors
setValidation :: SummonForm -> KitForm e -> KitForm e
setValidation field = setFieldValid (field `notElem` wrongFields) field
data FormError
= EmptyFields (NonEmpty SummonForm)
| OneWord (NonEmpty SummonForm)
| ProjectExist
| CabalOrStack
| LibOrExe
showFormError :: FormError -> String
showFormError = \case
ProjectExist -> "Directory with such name already exists"
CabalOrStack -> "Choose at least one: Cabal or Stack"
LibOrExe -> "Choose at least one: Library or Executable"
EmptyFields fields -> "These fields must not be empty: " ++ joinFields fields
OneWord fields -> "These fields should contain exactly one word: " ++ joinFields fields
where
joinFields :: NonEmpty SummonForm -> String
joinFields = intercalate ", " . mapMaybe showField . toList
showField :: SummonForm -> Maybe String
showField = \case
UserOwner -> Just "Owner"
UserFullName -> Just "Full name"
UserEmail -> Just "Email"
ProjectName -> Just "Name"
ProjectDesc -> Just "Description"
ProjectCat -> Just "Category"
CustomPreludeName -> Just "Prelude name"
CustomPreludeModule -> Just "Module"
_ -> Nothing
errorToInvalidFields :: FormError -> NonEmpty SummonForm
errorToInvalidFields = \case
EmptyFields fields -> fields
OneWord fields -> fields
ProjectExist -> one ProjectName
CabalOrStack -> CabalField :| [StackField]
LibOrExe -> Lib :| [Exe]
toError :: Bool -> e -> Validation (NonEmpty e) ()
toError p e = if p then Failure (one e) else Success ()
validateKit :: [FilePath] -> SummonKit -> Validation (NonEmpty FormError) ()
validateKit dirs kit =
validateEmpty
*> validateOneWord
*> validateProjectExist
*> validateBuildTools
*> validateLibOrExe
where
liftValidation
:: (e -> FormError)
-> Validation e ()
-> Validation (NonEmpty FormError) ()
liftValidation mkError = first (one . mkError)
validateEmpty :: Validation (NonEmpty FormError) ()
validateEmpty = liftValidation EmptyFields validateFields
where
validateFields :: Validation (NonEmpty SummonForm) ()
validateFields =
checkField (user . owner) UserOwner
*> checkField (user . fullName) UserFullName
*> checkField (user . email) UserEmail
*> checkField (project . repo) ProjectName
*> checkField (project . desc) ProjectDesc
*> toError isEmptyPrelude CustomPreludeModule
checkField :: Lens' SummonKit Text -> SummonForm -> Validation (NonEmpty SummonForm) ()
checkField textL = toError $ isEmpty $ kit ^. textL
isEmpty :: Text -> Bool
isEmpty t = T.strip t == ""
isEmptyPrelude :: Bool
isEmptyPrelude =
not (isEmpty $ kit ^. projectMeta . preludeName)
&& isEmpty (kit ^. projectMeta . preludeModule)
validateOneWord :: Validation (NonEmpty FormError) ()
validateOneWord = liftValidation OneWord validateFields
where
validateFields :: Validation (NonEmpty SummonForm) ()
validateFields =
checkField (user . owner) UserOwner
*> checkField (user . email) UserEmail
*> checkField (project . repo) ProjectName
*> checkField (projectMeta . preludeName) CustomPreludeName
*> checkField (projectMeta . preludeModule) CustomPreludeModule
checkField :: Lens' SummonKit Text -> SummonForm -> Validation (NonEmpty SummonForm) ()
checkField textL = toError (length (words $ kit ^. textL) > 1)
validateProjectExist :: Validation (NonEmpty FormError) ()
validateProjectExist = toError
(toString (kit ^. project . repo) `elem` dirs)
ProjectExist
validateBuildTools :: Validation (NonEmpty FormError) ()
validateBuildTools = toError
(not $ kit ^. cabal || kit ^. stack)
CabalOrStack
validateLibOrExe :: Validation (NonEmpty FormError) ()
validateLibOrExe = toError
(not $ kit ^. projectMeta . lib || kit ^. projectMeta . exe)
LibOrExe
formErrorMessages :: [FilePath] -> KitForm e -> [String]
formErrorMessages dirs kitForm = validatedErrorMessages ++ ghcErrorMessage
where
validatedErrorMessages :: [String]
validatedErrorMessages = case validateKit dirs (formState kitForm) of
Success _ -> []
Failure errors -> map showFormError $ toList errors
ghcErrorMessage :: [String]
ghcErrorMessage =
["Some GHC versions failed to parse: use space-separated valid GHC versions"
| Ghcs `elem` invalidFields kitForm
]