git.iko.soy/typeable/email-validate-extra
on master help sign in
Setup.hsdeleted−2
since splitsnapshots
unifiedsplit
@@ -1,2 +0,0 @@
1−import Distribution.Simple
2−main = defaultMain
email-validate-extra.cabal+41−6
@@ -6,15 +6,50 @@ license-file: LICENSE
6author: typeable.io
6author: typeable.io
7build-type: Simple
7build-type: Simple
8
8
9+Flag postgres
10+ Description: Add Postgresql instances to EmailAddress
11+ Default: False
12+ Manual: False
13+
14+Flag aeson
15+ Description: Add JSON instances to EmailAddress
16+ Default: True
17+ Manual: False
18+
19+Flag schema
20+ Description: Add openapi3 instances to EmailAddress
21+ Default: False
22+ Manual: False
23+
24+Flag arbitrary
25+ Description: Add Arbitrary instances to EmailAddress
26+ Default: True
27+ -- for backward compatibility ^
28+ Manual: False
29+
9library
30library
10 hs-source-dirs: src
31 hs-source-dirs: src
11 default-language: Haskell2010
32 default-language: Haskell2010
12 default-extensions: OverloadedStrings
33 default-extensions: OverloadedStrings
13− build-depends: base >=4.10 && <5.0
34+ build-depends: base >=4.10 && <5.0
14− , bytestring
35+ , bytestring
15− , email-validate
36+ , email-validate
16− , text
37+ , text
38+ if flag(arbitrary)
17− , QuickCheck
39+ build-depends: QuickCheck
40+ cpp-options: -DARBITRARY
41+ if flag(postgres) && !impl(ghcjs)
42+ build-depends: postgresql-simple
43+ , case-insensitive
44+
45+ cpp-options: -DPOSTGRES
46+ if flag(aeson)
47+ build-depends: aeson
48+ cpp-options: -DAESON
49+ if flag(schema)
50+ build-depends: openapi3
51+ cpp-options: -DSCHEMA
52+
18 exposed-modules: Text.Email.Extra
53 exposed-modules: Text.Email.Extra
19
54
20test-suite spec
55test-suite spec
@@ -25,4 +60,4 @@ test-suite spec
25 build-depends: base >= 4.10 && < 5.0
60 build-depends: base >= 4.10 && < 5.0
26 , email-validate-extra
61 , email-validate-extra
27 , hspec >= 2.2.0
62 , hspec >= 2.2.0
28− ghc-options: -Wall -O2
63+ ghc-options: -Wall -O2
src/Text/Email/Extra.hs+57−12
@@ -1,17 +1,34 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2+{-# LANGUAGE CPP #-}
3+
2module Text.Email.Extra
4module Text.Email.Extra
3 ( validateEmail
5 ( validateEmail
4 , emailToText
6 , emailToText
5 ) where
7 ) where
6
8
7−import Control.Monad ((<=<))
9+import Control.Monad ((<=<))
8−import Data.ByteString (ByteString)
10+import Data.ByteString (ByteString)
9−import qualified Data.ByteString.Char8 as ByteString
11+import qualified Data.ByteString.Char8 as ByteString
10−import Data.Text (Text)
12+import Data.Text (Text)
11−import qualified Data.Text.Encoding as Text
13+import qualified Data.Text.Encoding as Text
14+import qualified Text.Email.Validate as Email
15+import Text.Email.Validate (EmailAddress)
16+#ifdef ARBITRARY
12−import Test.QuickCheck
17+import Test.QuickCheck
13−import Text.Email.Validate (EmailAddress)
18+#endif
14−import qualified Text.Email.Validate as Email
19+#ifdef POSTGRES
20+import Data.CaseInsensitive as CI hiding (map)
21+import Database.PostgreSQL.Simple.FromField as PG
22+import Database.PostgreSQL.Simple.ToField as PG
23+#endif
24+#ifdef AESON
25+import Control.Monad
26+import Data.Aeson
27+#endif
28+#ifdef SCHEMA
29+import Data.OpenApi
30+import Data.Proxy
31+#endif
15
32
16-- | Validate that the domain part in this email address is
33-- | Validate that the domain part in this email address is
17-- in fact a domain name.
34-- in fact a domain name.
@@ -35,7 +52,7 @@ validateEmailDomain email
35 , length chunks >= 2
52 , length chunks >= 2
36 -- Check that there are no empty chunks. This would happen if the domain
53 -- Check that there are no empty chunks. This would happen if the domain
37 -- starts or ends with '.' or has two or more dots next to one another.
54 -- starts or ends with '.' or has two or more dots next to one another.
38− , none (ByteString.null) chunks
55+ , not $ any ByteString.null chunks
39 -- Finally, check that the last chunk (TLD) is two or more
56 -- Finally, check that the last chunk (TLD) is two or more
40 -- characters long, since there are no single-character top domains.
57 -- characters long, since there are no single-character top domains.
41 , ByteString.length (last chunks) >= 2 = True
58 , ByteString.length (last chunks) >= 2 = True
@@ -47,9 +64,7 @@ validateEmail = validateEmailDomain <=< Email.validate
47emailToText :: EmailAddress -> Text
64emailToText :: EmailAddress -> Text
48emailToText = Text.decodeUtf8 . Email.toByteString
65emailToText = Text.decodeUtf8 . Email.toByteString
49
66
50−none :: Foldable t => (a -> Bool) -> t a -> Bool
67+#ifdef ARBITRARY
51−none f = not . any f
52−
53instance Arbitrary EmailAddress where
68instance Arbitrary EmailAddress where
54 arbitrary = do
69 arbitrary = do
55 s <- take 64 <$> listOf1 (elements $ ['a'..'z'] <> ['A'..'Z'] <> ['0'..'9'])
70 s <- take 64 <$> listOf1 (elements $ ['a'..'z'] <> ['A'..'Z'] <> ['0'..'9'])
@@ -73,3 +88,33 @@ instance Arbitrary EmailAddress where
73 -- domain <- replicateM n (elements ['a'..'Z'])
88 -- domain <- replicateM n (elements ['a'..'Z'])
74 -- domainLiteral <- elements ["com", "travel", "net", "ru", "io"]
89 -- domainLiteral <- elements ["com", "travel", "net", "ru", "io"]
75 -- unsafeEmailAddress <$> local <*> domain <> "." domainLiteral
90 -- unsafeEmailAddress <$> local <*> domain <> "." domainLiteral
91+#endif
92+
93+#ifdef POSTGRES
94+instance ToField EmailAddress where
95+ toField = toField . CI.mk . emailToText
96+
97+instance FromField EmailAddress where
98+ fromField f mbs = do
99+ ci <- fromField f mbs
100+ case validateEmail $ Text.encodeUtf8 $ CI.original ci of
101+ Left _ -> returnError ConversionFailed f "EmailAddress is not parseable"
102+ Right a -> return a
103+#endif
104+
105+#ifdef AESON
106+instance ToJSON EmailAddress where
107+ toEncoding = toEncoding . emailToText
108+ toJSON = toJSON . emailToText
109+
110+instance FromJSON EmailAddress where
111+ parseJSON = parseJSON >=> either fail pure . validateEmail . Text.encodeUtf8
112+#endif
113+
114+#ifdef SCHEMA
115+instance ToSchema EmailAddress where
116+ declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy Text)
117+
118+instance ToParamSchema EmailAddress where
119+ toParamSchema _ = toParamSchema (Proxy :: Proxy Text)
120+#endif
stack.yamlnew file+4
@@ -0,0 +1,4 @@
1+resolver: lts-19.33
2+
3+packages:
4+ - .