packages feed

emailaddress 0.1.6.0 → 0.2.0.0

raw patch · 7 files changed

+350/−337 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Text.Email.Validate: canonicalizeEmail :: ByteString -> Maybe ByteString
- Text.Email.Validate: data EmailAddress
- Text.Email.Validate: domainPart :: EmailAddress -> ByteString
- Text.Email.Validate: emailAddress :: ByteString -> Maybe EmailAddress
- Text.Email.Validate: emailAddressFromString :: String -> Maybe EmailAddress
- Text.Email.Validate: emailAddressFromText :: Text -> Maybe EmailAddress
- Text.Email.Validate: isValid :: ByteString -> Bool
- Text.Email.Validate: localPart :: EmailAddress -> ByteString
- Text.Email.Validate: toByteString :: EmailAddress -> ByteString
- Text.Email.Validate: toText :: EmailAddress -> Text
- Text.Email.Validate: unsafeEmailAddress :: ByteString -> ByteString -> EmailAddress
- Text.Email.Validate: validate :: ByteString -> Either String EmailAddress
- Text.Email.Validate: validateFromString :: String -> Either String EmailAddress
- Text.Email.Validate: validateFromText :: Text -> Either String EmailAddress
+ Text.EmailAddress: EmailAddress :: EmailAddress -> EmailAddress
+ Text.EmailAddress: [unEmailAddress] :: EmailAddress -> EmailAddress
+ Text.EmailAddress: canonicalizeEmail :: ByteString -> Maybe ByteString
+ Text.EmailAddress: domainPart :: EmailAddress -> ByteString
+ Text.EmailAddress: emailAddress :: ByteString -> Maybe EmailAddress
+ Text.EmailAddress: emailAddressFromString :: String -> Maybe EmailAddress
+ Text.EmailAddress: emailAddressFromText :: Text -> Maybe EmailAddress
+ Text.EmailAddress: isValid :: ByteString -> Bool
+ Text.EmailAddress: localPart :: EmailAddress -> ByteString
+ Text.EmailAddress: newtype EmailAddress
+ Text.EmailAddress: toByteString :: EmailAddress -> ByteString
+ Text.EmailAddress: toText :: EmailAddress -> Text
+ Text.EmailAddress: unsafeEmailAddress :: ByteString -> ByteString -> EmailAddress
+ Text.EmailAddress: validate :: ByteString -> Either String EmailAddress
+ Text.EmailAddress: validateFromString :: String -> Either String EmailAddress
+ Text.EmailAddress: validateFromText :: Text -> Either String EmailAddress

Files

+ CHANGELOG.md view
@@ -0,0 +1,10 @@+# Change Log+All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](http://keepachangelog.com/).++## v0.2.0.0 - 2016-11-12++### Changed++- Changed from the `Text.Email.Validate` module name to `Text.EmailAddress`.
README.md view
@@ -2,7 +2,10 @@ emailaddress ============ -[![Hackage](https://img.shields.io/hackage/v/emailaddress.svg)](https://hackage.haskell.org/package/emailaddress) [![Build Status](https://secure.travis-ci.org/cdepillabout/emailaddress.svg)](http://travis-ci.org/cdepillabout/emailaddress)+[![Build Status](https://secure.travis-ci.org/cdepillabout/emailaddress.svg)](http://travis-ci.org/cdepillabout/emailaddress)+[![Hackage](https://img.shields.io/hackage/v/emailaddress.svg)](https://hackage.haskell.org/package/emailaddress)+[![Stackage LTS](http://stackage.org/package/emailaddress/badge/lts)](http://stackage.org/lts/package/emailaddress)+[![Stackage Nightly](http://stackage.org/package/emailaddress/badge/nightly)](http://stackage.org/nightly/package/emailaddress)  This Haskell module wraps around the [`email-validate`](https://hackage.haskell.org/package/email-validate) package,@@ -13,8 +16,8 @@ [FromJSON](https://hackage.haskell.org/package/aeson/docs/Data-Aeson.html#t:FromJSON). This allows us to use the `EmailAddress` type without fear of orphan instances. -This package exposes the module `Text.Email.Validate`, so it can be used as-a drop-in replacement for+This package exposes the module `Text.EmailAddress`. It can be used as a+drop-in replacement for [`email-validate`](https://hackage.haskell.org/package/email-validate).  If you would like to add new instances for `EmailAddress`, please send a pull
emailaddress.cabal view
@@ -1,5 +1,5 @@ name:                emailaddress-version:             0.1.6.0+version:             0.2.0.0 synopsis:            Wrapper around email-validate library adding instances for common type classes. description:         Please see README.md homepage:            https://github.com/cdepillabout/emailaddress#readme@@ -11,12 +11,13 @@ category:            Text build-type:          Simple extra-source-files:  README.md+                   , CHANGELOG.md cabal-version:       >=1.10  library   hs-source-dirs:      src-  exposed-modules:     Text.Email.Validate-  other-modules:       Text.Email.Validate.Internal+  exposed-modules:     Text.EmailAddress+  other-modules:       Text.EmailAddress.Internal   build-depends:       base >= 4.6 && < 5                      , aeson >= 0.9                      , bifunctors >= 5
− src/Text/Email/Validate.hs
@@ -1,51 +0,0 @@-{-|-Module      : Text.Email.Validate-Description : Wrapper around email-validate adding additional instances.-Copyright   : (c) Dennis Gosnell, 2016-License     : BSD3--This module is a wrapper around-<https://hackage.haskell.org/package/email-validate-2.2.0/docs/Text-Email-Validate.html Text.Email.Validate>-from <https://hackage.haskell.org/package/email-validate email-validate>.--This module exports 'EmailAddress', a newtype wrapper around-<https://hackage.haskell.org/package/email-validate/docs/Text-Email-Validate.html#t:EmailAddress Text.Email.Validate.EmailAddress>.-Additional instances are defined for our-new 'EmailAddress', including 'Data.Aeson.ToJSON' and 'Data.Aeson.FromJSON'. This is done so that no-orphan instances need to be used.--If you would like additional instances to be defined, please send a-<https://github.com/cdepillabout/emailaddress pull request>.  Additional-instances will be accepted for any typeclass from any package available on-stackage.--}--module Text.Email.Validate-    ( -- * Data Type-      EmailAddress-      -- * Create EmailAddress-    , emailAddress-    , validate-    , emailAddressFromText-    , validateFromText-    , emailAddressFromString-    , validateFromString-      -- * Check validity-    , isValid-      -- * Convert to Text-    , toText-      -- * Convert back to ByteString-    , toByteString-    , localPart-    , domainPart-      -- * Helper functions-    , canonicalizeEmail-      -- * Unsafe creation-    , unsafeEmailAddress-    ) where--import Text.Email.Validate.Internal-    ( EmailAddress, canonicalizeEmail, domainPart, emailAddress-    , emailAddressFromString, emailAddressFromText, isValid, localPart-    , toByteString, toText, validate, validateFromString, validateFromText-    , unsafeEmailAddress )
− src/Text/Email/Validate/Internal.hs
@@ -1,280 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE InstanceSigs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PackageImports #-}--module Text.Email.Validate.Internal-    ( EmailAddress(unEmailAddress)-    , EmailValidate.isValid-    , EmailValidate.canonicalizeEmail-    , emailAddress-    , validate-    , emailAddressFromText-    , validateFromText-    , emailAddressFromString-    , validateFromString-    , unsafeEmailAddress-    , localPart-    , domainPart-    , toByteString-    , toText-    ) where--import Control.Monad ((<=<))-import Data.Aeson (FromJSON(..), ToJSON(..), Value(..), withText)-import Data.Aeson.Types (Parser)-import Data.Bifunctor (first)-import Data.ByteString (ByteString)-import Data.Data (Data)-import Data.Monoid ((<>))-import Data.Profunctor (lmap)-import Data.Profunctor.Product.Default (Default(def))-import Data.Proxy (Proxy(Proxy))-import Data.Text (Text, pack)-import Data.Text.Encoding (decodeUtf8With, encodeUtf8)-import Data.Text.Encoding.Error (lenientDecode)-import Database.Persist (PersistField(..), PersistValue)-import Database.Persist.Sql (PersistFieldSql(..), SqlType)-import Database.PostgreSQL.Simple.FromField-    ( Conversion, FieldParser, FromField(..), ResultError(..), returnError )-import Data.Typeable (Typeable)-import GHC.Generics (Generic)-import Opaleye-    ( Column, Constant(..), PGText, QueryRunnerColumn-    , QueryRunnerColumnDefault(..) , fieldQueryRunnerColumn )-import Text.Read (Read(readPrec), ReadPrec)-import Web.HttpApiData-    ( FromHttpApiData(parseUrlPiece), ToHttpApiData(toUrlPiece) )-import Web.PathPieces (PathPiece(fromPathPiece, toPathPiece))--import qualified "email-validate" Text.Email.Validate as EmailValidate---- | Type to represent an email address.  Newtype wrapper around--- 'EmailValidate.EmailAddress' with additional typeclass instances.-newtype EmailAddress = EmailAddress-    { unEmailAddress :: EmailValidate.EmailAddress }-    deriving (Data, Eq, Generic, Ord, Typeable)--instance Default Constant EmailAddress (Column PGText) where-    def :: Constant EmailAddress (Column PGText)-    def = lmap (decodeUtf8With lenientDecode . toByteString) def--instance FromField EmailAddress where-    fromField :: FieldParser EmailAddress-    -- fromField :: Field -> Maybe ByteString -> Conversion EmailAddress-    fromField field Nothing = returnError UnexpectedNull field ""-    fromField field (Just email) = maybe err return $ emailAddress email-      where-        err :: Conversion EmailAddress-        err = returnError ConversionFailed field $-            "Could not convert " <> show email <> " to email address"---- | This instance assumes 'EmailAddress' is UTF8-encoded.  See--- 'validateFromText'.------ >>> import Data.Either (isLeft)--- >>> fmap toText $ parseUrlPiece "foo@gmail.com"--- Right "foo@gmail.com"--- >>> isLeft $ (parseUrlPiece "not an email address" :: Either Text EmailAddress)--- True-instance FromHttpApiData EmailAddress where-    parseUrlPiece :: Text -> Either Text EmailAddress-    parseUrlPiece = first pack . validateFromText---- | Parse 'EmailAddress' from JSON.------ >>> import Data.Aeson (decode)--- >>> fmap (fmap toText) (decode "[ \"foo@gmail.com \" ]" :: Maybe [EmailAddress])--- Just ["foo@gmail.com"]--- >>> decode "[ \"not an email address\" ]" :: Maybe [EmailAddress]--- Nothing-instance FromJSON EmailAddress where-    parseJSON :: Value -> Parser EmailAddress-    parseJSON = withText "EmailAddress" $ \t ->-                    case validate $ encodeUtf8 t of-                        Left err -> fail $ "Failed to parse email address: " <> err-                        Right email -> return email-    {-# INLINE parseJSON #-}---- | See 'emailAddressFromText' and 'toText'.------ >>> fmap toText $ fromPathPiece "foo@gmail.com"--- Just "foo@gmail.com"--- >>> fmap toText $ fromPathPiece "this is not an email address"--- Nothing--- >>> toPathPiece $ unsafeEmailAddress "foo" "gmail.com"--- "foo@gmail.com"-instance PathPiece EmailAddress where-    fromPathPiece :: Text -> Maybe EmailAddress-    fromPathPiece = emailAddressFromText--    toPathPiece :: EmailAddress -> Text-    toPathPiece = toText---- | Treat 'EmailAddress' just like a 'Text' value.------ >>> import Data.Either (isLeft)--- >>> import Database.Persist.Types (PersistValue(PersistBool, PersistText))--- >>> toPersistValue $ unsafeEmailAddress "foo" "gmail.com"--- PersistText "foo@gmail.com"--- >>> fmap toText $ fromPersistValue (PersistText "foo@gmail.com")--- Right "foo@gmail.com"--- >>> isLeft (fromPersistValue (PersistText "not an email address") :: Either Text EmailAddress)--- True--- >>> isLeft (fromPersistValue (PersistBool False) :: Either Text EmailAddress)--- True-instance PersistField EmailAddress where-    toPersistValue :: EmailAddress -> PersistValue-    toPersistValue = toPersistValue . toText--    fromPersistValue :: PersistValue -> Either Text EmailAddress-    fromPersistValue = first pack . validateFromText <=< fromPersistValue---- | Treat 'EmailAddress' just like a 'Text' value.------ >>> sqlType (Proxy :: Proxy EmailAddress)--- SqlString-instance PersistFieldSql EmailAddress where-    sqlType :: Proxy EmailAddress -> SqlType-    sqlType _ = sqlType (Proxy :: Proxy Text)--instance QueryRunnerColumnDefault PGText EmailAddress where-    queryRunnerColumnDefault :: QueryRunnerColumn PGText EmailAddress-    queryRunnerColumnDefault = fieldQueryRunnerColumn---- |--- >>> toText $ read "\"foo@gmail.com\""--- "foo@gmail.com"-instance Read EmailAddress where-    readPrec :: ReadPrec EmailAddress-    readPrec = fmap EmailAddress readPrec---- |--- >>> show $ unsafeEmailAddress "foo" "gmail.com"--- "\"foo@gmail.com\""-instance Show EmailAddress where-    show :: EmailAddress -> String-    show = show . unEmailAddress---- | Turn 'EmailAddress' into JSON.------ >>> toJSON $ unsafeEmailAddress "foo" "gmail.com"--- String "foo@gmail.com"-instance ToJSON EmailAddress where-    toJSON :: EmailAddress -> Value-    toJSON = String . decodeUtf8With lenientDecode . toByteString---- | This instance assumes 'EmailAddress' is UTF8-encoded.  See 'toText'.------ >>> toUrlPiece $ unsafeEmailAddress "foo" "gmail.com"--- "foo@gmail.com"-instance ToHttpApiData EmailAddress where-      toUrlPiece = toText---- | Wrapper around 'EmailValidate.validate'.------ >>> validate "foo@gmail.com"--- Right "foo@gmail.com"--- >>> import Data.Either (isLeft)--- >>> isLeft $ validate "not an email address"--- True-validate :: ByteString -> Either String EmailAddress-validate = fmap EmailAddress . EmailValidate.validate---- | Wrapper around 'EmailValidate.emailAddress'.------ Similar to 'validate', but returns 'Nothing' if the email address fails to--- parse.------ >>> emailAddress "foo@gmail.com"--- Just "foo@gmail.com"--- >>> emailAddress "not an email address"--- Nothing-emailAddress :: ByteString -> Maybe EmailAddress-emailAddress = fmap EmailAddress . EmailValidate.emailAddress---- | Create an 'EmailAddress' from a 'Text' value.  See 'validate'.-validateFromText :: Text -> Either String EmailAddress-validateFromText = validate . encodeUtf8---- | Create an 'EmailAddress' from a 'Text' value.  See 'emailAddress'.-emailAddressFromText :: Text -> Maybe EmailAddress-emailAddressFromText = emailAddress . encodeUtf8---- | Create an 'EmailAddress' from a 'String' value.  See 'validate'.-validateFromString :: String -> Either String EmailAddress-validateFromString = validateFromText . pack---- | Create an 'EmailAddress' from a 'String' value.  See 'emailAddress'.-emailAddressFromString :: String -> Maybe EmailAddress-emailAddressFromString = emailAddressFromText . pack---- | Wrapper around 'EmailValidate.unsafeEmailAddress'.------ Unsafely create an 'EmailAddress' from a local part and a domain part.  The--- first argument is the local part, and the second argument is the domain--- part.------ For example, in the email address @foo\@gmail.com@, the local part is @foo@--- and the domain part is @gmail.com@.------ >>> unsafeEmailAddress "foo" "gmail.com"--- "foo@gmail.com"-unsafeEmailAddress-    :: ByteString    -- ^ Local part-    -> ByteString    -- ^ Domain part-    -> EmailAddress-unsafeEmailAddress = (EmailAddress .) . EmailValidate.unsafeEmailAddress---- | Wrapper around 'EmailValidate.localPart'.------ Extracts the local part from an email address.------ For example, in the email address @foo\@gmail.com@, the local part is @foo@.------ >>> let email = unsafeEmailAddress "foo" "gmail.com"--- >>> email--- "foo@gmail.com"--- >>> localPart email--- "foo"-localPart :: EmailAddress -> ByteString-localPart = EmailValidate.localPart . unEmailAddress---- | Wrapper around 'EmailValidate.domainPart'.------ Extracts the domain part from an email address.------ For example, in the email address @foo\@gmail.com@, the domain part is--- @gmail.com@.------ >>> let email = unsafeEmailAddress "foo" "gmail.com"--- >>> email--- "foo@gmail.com"--- >>> domainPart email--- "gmail.com"-domainPart :: EmailAddress -> ByteString-domainPart = EmailValidate.domainPart . unEmailAddress---- | Wrapper around 'EmailValidate.toByteString'.------ >>> let email = unsafeEmailAddress "foo" "gmail.com"--- >>> email--- "foo@gmail.com"--- >>> toByteString email--- "foo@gmail.com"-toByteString :: EmailAddress -> ByteString-toByteString = EmailValidate.toByteString . unEmailAddress---- | Convert an email address to 'Text'.------ This assumes the 'EmailAddress' is UTF8-encoded.------ >>> let email = unsafeEmailAddress "foo" "gmail.com"--- >>> email--- "foo@gmail.com"--- >>> toText email--- "foo@gmail.com"-toText :: EmailAddress -> Text-toText = decodeUtf8With lenientDecode . toByteString
+ src/Text/EmailAddress.hs view
@@ -0,0 +1,51 @@+{-|+Module      : Text.EmailAddress+Description : Wrapper around email-validate adding additional instances.+Copyright   : (c) Dennis Gosnell, 2016+License     : BSD3++This module is a wrapper around+<https://hackage.haskell.org/package/email-validate-2.2.0/docs/Text-Email-Validate.html Text.Email.Validate>+from <https://hackage.haskell.org/package/email-validate email-validate>.++This module exports 'EmailAddress', a newtype wrapper around+<https://hackage.haskell.org/package/email-validate/docs/Text-Email-Validate.html#t:EmailAddress Text.Email.Validate.EmailAddress>.+Additional instances are defined for our+new 'EmailAddress', including 'Data.Aeson.ToJSON' and 'Data.Aeson.FromJSON'. This is done so that no+orphan instances need to be used.++If you would like additional instances to be defined, please send a+<https://github.com/cdepillabout/emailaddress pull request>.  Additional+instances will be accepted for any typeclass from any package available on+stackage.+-}++module Text.EmailAddress+    ( -- * Data Type+      EmailAddress(EmailAddress, unEmailAddress)+      -- * Create EmailAddress+    , emailAddress+    , validate+    , emailAddressFromText+    , validateFromText+    , emailAddressFromString+    , validateFromString+      -- * Check validity+    , isValid+      -- * Convert to Text+    , toText+      -- * Convert back to ByteString+    , toByteString+    , localPart+    , domainPart+      -- * Helper functions+    , canonicalizeEmail+      -- * Unsafe creation+    , unsafeEmailAddress+    ) where++import Text.EmailAddress.Internal+       (EmailAddress(EmailAddress, unEmailAddress), canonicalizeEmail,+        domainPart, emailAddress, emailAddressFromString,+        emailAddressFromText, isValid, localPart, toByteString, toText,+        validate, validateFromString, validateFromText, unsafeEmailAddress)
+ src/Text/EmailAddress/Internal.hs view
@@ -0,0 +1,279 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MultiParamTypeClasses #-}++module Text.EmailAddress.Internal+    ( EmailAddress(EmailAddress, unEmailAddress)+    , EmailValidate.isValid+    , EmailValidate.canonicalizeEmail+    , emailAddress+    , validate+    , emailAddressFromText+    , validateFromText+    , emailAddressFromString+    , validateFromString+    , unsafeEmailAddress+    , localPart+    , domainPart+    , toByteString+    , toText+    ) where++import Control.Monad ((<=<))+import Data.Aeson (FromJSON(..), ToJSON(..), Value(..), withText)+import Data.Aeson.Types (Parser)+import Data.Bifunctor (first)+import Data.ByteString (ByteString)+import Data.Data (Data)+import Data.Monoid ((<>))+import Data.Profunctor (lmap)+import Data.Profunctor.Product.Default (Default(def))+import Data.Proxy (Proxy(Proxy))+import Data.Text (Text, pack)+import Data.Text.Encoding (decodeUtf8With, encodeUtf8)+import Data.Text.Encoding.Error (lenientDecode)+import Database.Persist (PersistField(..), PersistValue)+import Database.Persist.Sql (PersistFieldSql(..), SqlType)+import Database.PostgreSQL.Simple.FromField+    ( Conversion, FieldParser, FromField(..), ResultError(..), returnError )+import Data.Typeable (Typeable)+import GHC.Generics (Generic)+import Opaleye+    ( Column, Constant(..), PGText, QueryRunnerColumn+    , QueryRunnerColumnDefault(..) , fieldQueryRunnerColumn )+import Text.Read (Read(readPrec), ReadPrec)+import Web.HttpApiData+    ( FromHttpApiData(parseUrlPiece), ToHttpApiData(toUrlPiece) )+import Web.PathPieces (PathPiece(fromPathPiece, toPathPiece))++import qualified Text.Email.Validate as EmailValidate++-- | Type to represent an email address.  Newtype wrapper around+-- 'EmailValidate.EmailAddress' with additional typeclass instances.+newtype EmailAddress = EmailAddress+    { unEmailAddress :: EmailValidate.EmailAddress }+    deriving (Data, Eq, Generic, Ord, Typeable)++instance Default Constant EmailAddress (Column PGText) where+    def :: Constant EmailAddress (Column PGText)+    def = lmap (decodeUtf8With lenientDecode . toByteString) def++instance FromField EmailAddress where+    fromField :: FieldParser EmailAddress+    -- fromField :: Field -> Maybe ByteString -> Conversion EmailAddress+    fromField field Nothing = returnError UnexpectedNull field ""+    fromField field (Just email) = maybe err return $ emailAddress email+      where+        err :: Conversion EmailAddress+        err = returnError ConversionFailed field $+            "Could not convert " <> show email <> " to email address"++-- | This instance assumes 'EmailAddress' is UTF8-encoded.  See+-- 'validateFromText'.+--+-- >>> import Data.Either (isLeft)+-- >>> fmap toText $ parseUrlPiece "foo@gmail.com"+-- Right "foo@gmail.com"+-- >>> isLeft $ (parseUrlPiece "not an email address" :: Either Text EmailAddress)+-- True+instance FromHttpApiData EmailAddress where+    parseUrlPiece :: Text -> Either Text EmailAddress+    parseUrlPiece = first pack . validateFromText++-- | Parse 'EmailAddress' from JSON.+--+-- >>> import Data.Aeson (decode)+-- >>> fmap (fmap toText) (decode "[ \"foo@gmail.com \" ]" :: Maybe [EmailAddress])+-- Just ["foo@gmail.com"]+-- >>> decode "[ \"not an email address\" ]" :: Maybe [EmailAddress]+-- Nothing+instance FromJSON EmailAddress where+    parseJSON :: Value -> Parser EmailAddress+    parseJSON = withText "EmailAddress" $ \t ->+                    case validate $ encodeUtf8 t of+                        Left err -> fail $ "Failed to parse email address: " <> err+                        Right email -> return email+    {-# INLINE parseJSON #-}++-- | See 'emailAddressFromText' and 'toText'.+--+-- >>> fmap toText $ fromPathPiece "foo@gmail.com"+-- Just "foo@gmail.com"+-- >>> fmap toText $ fromPathPiece "this is not an email address"+-- Nothing+-- >>> toPathPiece $ unsafeEmailAddress "foo" "gmail.com"+-- "foo@gmail.com"+instance PathPiece EmailAddress where+    fromPathPiece :: Text -> Maybe EmailAddress+    fromPathPiece = emailAddressFromText++    toPathPiece :: EmailAddress -> Text+    toPathPiece = toText++-- | Treat 'EmailAddress' just like a 'Text' value.+--+-- >>> import Data.Either (isLeft)+-- >>> import Database.Persist.Types (PersistValue(PersistBool, PersistText))+-- >>> toPersistValue $ unsafeEmailAddress "foo" "gmail.com"+-- PersistText "foo@gmail.com"+-- >>> fmap toText $ fromPersistValue (PersistText "foo@gmail.com")+-- Right "foo@gmail.com"+-- >>> isLeft (fromPersistValue (PersistText "not an email address") :: Either Text EmailAddress)+-- True+-- >>> isLeft (fromPersistValue (PersistBool False) :: Either Text EmailAddress)+-- True+instance PersistField EmailAddress where+    toPersistValue :: EmailAddress -> PersistValue+    toPersistValue = toPersistValue . toText++    fromPersistValue :: PersistValue -> Either Text EmailAddress+    fromPersistValue = first pack . validateFromText <=< fromPersistValue++-- | Treat 'EmailAddress' just like a 'Text' value.+--+-- >>> sqlType (Proxy :: Proxy EmailAddress)+-- SqlString+instance PersistFieldSql EmailAddress where+    sqlType :: Proxy EmailAddress -> SqlType+    sqlType _ = sqlType (Proxy :: Proxy Text)++instance QueryRunnerColumnDefault PGText EmailAddress where+    queryRunnerColumnDefault :: QueryRunnerColumn PGText EmailAddress+    queryRunnerColumnDefault = fieldQueryRunnerColumn++-- |+-- >>> toText $ read "\"foo@gmail.com\""+-- "foo@gmail.com"+instance Read EmailAddress where+    readPrec :: ReadPrec EmailAddress+    readPrec = fmap EmailAddress readPrec++-- |+-- >>> show $ unsafeEmailAddress "foo" "gmail.com"+-- "\"foo@gmail.com\""+instance Show EmailAddress where+    show :: EmailAddress -> String+    show = show . unEmailAddress++-- | Turn 'EmailAddress' into JSON.+--+-- >>> toJSON $ unsafeEmailAddress "foo" "gmail.com"+-- String "foo@gmail.com"+instance ToJSON EmailAddress where+    toJSON :: EmailAddress -> Value+    toJSON = String . decodeUtf8With lenientDecode . toByteString++-- | This instance assumes 'EmailAddress' is UTF8-encoded.  See 'toText'.+--+-- >>> toUrlPiece $ unsafeEmailAddress "foo" "gmail.com"+-- "foo@gmail.com"+instance ToHttpApiData EmailAddress where+      toUrlPiece = toText++-- | Wrapper around 'EmailValidate.validate'.+--+-- >>> validate "foo@gmail.com"+-- Right "foo@gmail.com"+-- >>> import Data.Either (isLeft)+-- >>> isLeft $ validate "not an email address"+-- True+validate :: ByteString -> Either String EmailAddress+validate = fmap EmailAddress . EmailValidate.validate++-- | Wrapper around 'EmailValidate.emailAddress'.+--+-- Similar to 'validate', but returns 'Nothing' if the email address fails to+-- parse.+--+-- >>> emailAddress "foo@gmail.com"+-- Just "foo@gmail.com"+-- >>> emailAddress "not an email address"+-- Nothing+emailAddress :: ByteString -> Maybe EmailAddress+emailAddress = fmap EmailAddress . EmailValidate.emailAddress++-- | Create an 'EmailAddress' from a 'Text' value.  See 'validate'.+validateFromText :: Text -> Either String EmailAddress+validateFromText = validate . encodeUtf8++-- | Create an 'EmailAddress' from a 'Text' value.  See 'emailAddress'.+emailAddressFromText :: Text -> Maybe EmailAddress+emailAddressFromText = emailAddress . encodeUtf8++-- | Create an 'EmailAddress' from a 'String' value.  See 'validate'.+validateFromString :: String -> Either String EmailAddress+validateFromString = validateFromText . pack++-- | Create an 'EmailAddress' from a 'String' value.  See 'emailAddress'.+emailAddressFromString :: String -> Maybe EmailAddress+emailAddressFromString = emailAddressFromText . pack++-- | Wrapper around 'EmailValidate.unsafeEmailAddress'.+--+-- Unsafely create an 'EmailAddress' from a local part and a domain part.  The+-- first argument is the local part, and the second argument is the domain+-- part.+--+-- For example, in the email address @foo\@gmail.com@, the local part is @foo@+-- and the domain part is @gmail.com@.+--+-- >>> unsafeEmailAddress "foo" "gmail.com"+-- "foo@gmail.com"+unsafeEmailAddress+    :: ByteString    -- ^ Local part+    -> ByteString    -- ^ Domain part+    -> EmailAddress+unsafeEmailAddress = (EmailAddress .) . EmailValidate.unsafeEmailAddress++-- | Wrapper around 'EmailValidate.localPart'.+--+-- Extracts the local part from an email address.+--+-- For example, in the email address @foo\@gmail.com@, the local part is @foo@.+--+-- >>> let email = unsafeEmailAddress "foo" "gmail.com"+-- >>> email+-- "foo@gmail.com"+-- >>> localPart email+-- "foo"+localPart :: EmailAddress -> ByteString+localPart = EmailValidate.localPart . unEmailAddress++-- | Wrapper around 'EmailValidate.domainPart'.+--+-- Extracts the domain part from an email address.+--+-- For example, in the email address @foo\@gmail.com@, the domain part is+-- @gmail.com@.+--+-- >>> let email = unsafeEmailAddress "foo" "gmail.com"+-- >>> email+-- "foo@gmail.com"+-- >>> domainPart email+-- "gmail.com"+domainPart :: EmailAddress -> ByteString+domainPart = EmailValidate.domainPart . unEmailAddress++-- | Wrapper around 'EmailValidate.toByteString'.+--+-- >>> let email = unsafeEmailAddress "foo" "gmail.com"+-- >>> email+-- "foo@gmail.com"+-- >>> toByteString email+-- "foo@gmail.com"+toByteString :: EmailAddress -> ByteString+toByteString = EmailValidate.toByteString . unEmailAddress++-- | Convert an email address to 'Text'.+--+-- This assumes the 'EmailAddress' is UTF8-encoded.+--+-- >>> let email = unsafeEmailAddress "foo" "gmail.com"+-- >>> email+-- "foo@gmail.com"+-- >>> toText email+-- "foo@gmail.com"+toText :: EmailAddress -> Text+toText = decodeUtf8With lenientDecode . toByteString