-
Notifications
You must be signed in to change notification settings - Fork 1
/
Main.hs
185 lines (164 loc) · 6.02 KB
/
Main.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
module Main
( main
) where
import Data.List.NonEmpty (NonEmpty ((:|)))
import Control.Applicative (Applicative (liftA2))
import Data.Char (isDigit, isSpace)
import Data.Ix (Ix (inRange))
import Valida (Validation, Validator (..), failureUnless', label, lengthWithin, minLengthOf, mustContain, optionally,
valueWithin, (-?>), (</>))
data InputForm = InpForm
{ inpName :: String
, inpAge :: Int
, inpEmail :: String
, inpPhone :: Maybe String
} deriving (Eq, Show)
data FormErr
= InvalidNameLength
| InvalidAge
| NoAtCharInMail
| NoPeriodInMail
| InvalidEmailLength
| InvalidPhLen
| IncorrectPhFormat
deriving (Show)
neSingleton :: a -> NonEmpty a
neSingleton = (:|[])
-- | Validator for each field in the input form - built using 'Validator' combinators.
inpFormValidator :: Validator (NonEmpty FormErr) InputForm InputForm
inpFormValidator = InpForm
-- Name should be between 1 and 20 characters long
<$> inpName -?> lengthWithin (1, 20) InvalidNameLength
-- Age should be between 18 and 120
<*> inpAge -?> valueWithin (18, 120) InvalidAge
-- Email should contain '@', and '.', and be atleast 5 characters long
<*> inpEmail -?> (mustContain '@' NoAtCharInMail
<> mustContain '.' NoPeriodInMail
<> minLengthOf 5 InvalidEmailLength)
-- Phone may not be provided, if it is - it should be 15 characters long, and correctly formatted
<*> inpPhone -?> optionally
( lengthWithin (14, 15) InvalidPhLen
-- Either Intl format or NA format
<> label (neSingleton IncorrectPhFormat)
(failureUnless' isCorrectPhIntl </> failureUnless' isCorrectPhNA)
)
where
-- | Format: \+[0-9] [2-9][0-9 ]+
isCorrectPhIntl ('+':c:' ':ac:rest) = isDigit c && inRange ('2', '9') ac && all (liftA2 (||) isSpace isDigit) rest
isCorrectPhIntl _ = False
-- | Format: \([2-9][0-9]{2}\) [0-9 ]+
isCorrectPhNA ('(':ac:c:c':')':rest) = inRange ('2', '9') ac && all (liftA2 (||) isSpace isDigit) (c:c':rest)
isCorrectPhNA _ = False
---------------------------------------------------------------------
-- Examples usage
---------------------------------------------------------------------
-- Failure (InvalidNameLength :| [])
emptyName :: Validation (NonEmpty FormErr) InputForm
emptyName = runValidator inpFormValidator $ InpForm
{ inpName = ""
, inpAge = 42
, inpEmail = "johndoe@e.mail"
, inpPhone = Nothing
}
-- Failure (InvalidNameLength :| [])
longName :: Validation (NonEmpty FormErr) InputForm
longName = runValidator inpFormValidator $ InpForm
{ inpName = "an incredibly long name"
, inpAge = 42
, inpEmail = "johndoe@e.mail"
, inpPhone = Nothing
}
-- Failure (InvalidAge :| [])
underAge :: Validation (NonEmpty FormErr) InputForm
underAge = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 0
, inpEmail = "johndoe@e.mail"
, inpPhone = Nothing
}
-- Failure (InvalidAge :| [])
overAge :: Validation (NonEmpty FormErr) InputForm
overAge = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 150
, inpEmail = "johndoe@e.mail"
, inpPhone = Nothing
}
-- Failure (NoAtCharInMail :| [])
noAtEmail :: Validation (NonEmpty FormErr) InputForm
noAtEmail = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "john.doe"
, inpPhone = Nothing
}
-- Failure (NoPeriodInMail :| [])
noDotEmail :: Validation (NonEmpty FormErr) InputForm
noDotEmail = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "john@doe"
, inpPhone = Nothing
}
-- Failure (NoAtCharInMail :| [])
noAtDotEmail :: Validation (NonEmpty FormErr) InputForm
noAtDotEmail = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "johndoeemail"
, inpPhone = Nothing
}
-- Failure (InvalidEmailLength :| [])
smallEmail :: Validation (NonEmpty FormErr) InputForm
smallEmail = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "@."
, inpPhone = Nothing
}
-- Failure (InvalidPhLen :| [])
smallPh :: Validation (NonEmpty FormErr) InputForm
smallPh = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "johndoe@e.mail"
, inpPhone = Just "+1 421"
}
-- Failure (IncorrectPhFormat :| [])
badPh :: Validation (NonEmpty FormErr) InputForm
badPh = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "johndoe@e.mail"
, inpPhone = Just " c abd 452 3670"
}
-- Success (InpForm {inpName = "John Doe", inpAge = 42, inpEmail = "johndoe@e.mail", inpPhone = Nothing})
correctWithNoPh :: Validation (NonEmpty FormErr) InputForm
correctWithNoPh = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "johndoe@e.mail"
, inpPhone = Nothing
}
-- Success (InpForm {inpName = "John Doe", inpAge = 42, inpEmail = "johndoe@e.mail", inpPhone = Just "+1 421 123 4567"})
correctWithPh :: Validation (NonEmpty FormErr) InputForm
correctWithPh = runValidator inpFormValidator $ InpForm
{ inpName = "John Doe"
, inpAge = 42
, inpEmail = "johndoe@e.mail"
, inpPhone = Just "+1 421 123 4567"
}
main :: IO ()
main = do
putStrLn $ "Empty name: " ++ show emptyName
putStrLn $ "Long name: " ++ show longName
putStrLn $ "Age too low: " ++ show underAge
putStrLn $ "Age too high: " ++ show overAge
putStrLn $ "No '@' in email: " ++ show noAtEmail
putStrLn $ "No '.' in email: " ++ show noDotEmail
putStrLn $ "No '@' or '.' in email: " ++ show noAtDotEmail
putStrLn $ "Too small email: " ++ show smallEmail
putStrLn $ "Too small phone number: " ++ show smallPh
putStrLn $ "Incorrect phone number: " ++ show badPh
putStrLn $ "All correct (no phone): " ++ show correctWithNoPh
putStrLn $ "All correct (with phone): " ++ show correctWithPh