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 +10/−0
- README.md +6/−3
- emailaddress.cabal +4/−3
- src/Text/Email/Validate.hs +0/−51
- src/Text/Email/Validate/Internal.hs +0/−280
- src/Text/EmailAddress.hs +51/−0
- src/Text/EmailAddress/Internal.hs +279/−0
+ 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 ============ -[](https://hackage.haskell.org/package/emailaddress) [](http://travis-ci.org/cdepillabout/emailaddress)+[](http://travis-ci.org/cdepillabout/emailaddress)+[](https://hackage.haskell.org/package/emailaddress)+[](http://stackage.org/lts/package/emailaddress)+[](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