packages feed

data-validation-0.1.2.0: examples/Examples/Data/ComplexTypes.hs

{-# LANGUAGE
    MultiParamTypeClasses
  , TemplateHaskellQuotes
#-}

module Examples.Data.ComplexTypes where

------------------------------------------------------------------------------------------------------------------------
import Data.Validation
------------------------------------------------------------------------------------------------------------------------
import Examples.Data.Primitives
------------------------------------------------------------------------------------------------------------------------

-- View Model
data UserVM
  = UserVM
  { userVMUsername          :: Maybe String
  , userVMEmailAddress      :: Maybe String
  , userVMPhoneNumber       :: Maybe String
  , userVMContactPreference :: Maybe ContactPreference
  , userVMZipCode           :: Maybe String
  } deriving (Show)

-- Actual Model
data User
  = User
  { userUsername          :: Username
  , userEmailAddress      :: Maybe EmailAddress
  , userPhoneNumber       :: Maybe PhoneNumber
  , userContactPreference :: ContactPreference
  , userZipCode           :: Maybe ZipCode
  } deriving (Show)

instance Validatable MyFailures UserVM User where
  validation u = do
    let vn = withField 'userVMUsername (userVMUsername u) $
          \n -> isRequired RequiredFailure n
          >>= refuteWithProof mkUsername
        -- EmailAdress and PhoneNumber are only required when ContactPreference matches
        ve = withField 'userVMEmailAddress (userVMEmailAddress u) $
          \me -> isRequiredWhen RequiredFailure (userVMContactPreference u == Just Email) me
          >>= whenJust (refuteWithProof mkEmailAddress)
        vp = withField 'userVMPhoneNumber (userVMPhoneNumber u) $
            \mp -> isRequiredWhen RequiredFailure (userVMContactPreference u == Just Phone) mp
            >>= whenJust (refuteWithProof mkPhoneNumber)
        cp = withField 'userVMContactPreference (userVMContactPreference u) $
          \p -> isRequired RequiredFailure p
        -- ZipCode is completely optional
        zc = optional (userVMZipCode u) $ \z ->
          withField 'userVMZipCode z $ refuteWithProof mkZipCode
        otherCheck = withValue u check
    pure User <*> vn <*> ve <*> vp <*> cp <*> zc <! otherCheck
    where
      -- arbitrary check
      check = disputeWithFact OtherFailure (\v -> userVMUsername v /= userVMEmailAddress v)