module Text.Email.Validate
    ( isValid
    , validate
    , emailAddress
    , canonicalizeEmail

    -- Re-exports:
    , EmailAddress
    , domainPart
    , localPart
    , toByteString
    , unsafeEmailAddress
    )
where

import Data.Attoparsec.ByteString (endOfInput, parseOnly)
import Data.ByteString (ByteString)

import Text.Email.Parser
    ( EmailAddress
    , addrSpec
    , domainPart
    , localPart
    , toByteString
    , unsafeEmailAddress)

-- | Smart constructor for an email address
emailAddress :: ByteString -> Maybe EmailAddress
emailAddress :: ByteString -> Maybe EmailAddress
emailAddress = (String -> Maybe EmailAddress)
-> (EmailAddress -> Maybe EmailAddress)
-> Either String EmailAddress
-> Maybe EmailAddress
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Maybe EmailAddress -> String -> Maybe EmailAddress
forall a b. a -> b -> a
const Maybe EmailAddress
forall a. Maybe a
Nothing) EmailAddress -> Maybe EmailAddress
forall a. a -> Maybe a
Just (Either String EmailAddress -> Maybe EmailAddress)
-> (ByteString -> Either String EmailAddress)
-> ByteString
-> Maybe EmailAddress
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String EmailAddress
validate

-- | Checks that an email is valid and returns a version of it
--   where comments and whitespace have been removed.
--
-- Example (requires `OverloadedStrings` to be enabled):
--
-- >>> canonicalizeEmail "spaces. are. allowed@example.com"
-- Just "spaces.are.allowed@example.com"
canonicalizeEmail :: ByteString -> Maybe ByteString
canonicalizeEmail :: ByteString -> Maybe ByteString
canonicalizeEmail = (EmailAddress -> ByteString)
-> Maybe EmailAddress -> Maybe ByteString
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap EmailAddress -> ByteString
toByteString (Maybe EmailAddress -> Maybe ByteString)
-> (ByteString -> Maybe EmailAddress)
-> ByteString
-> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Maybe EmailAddress
emailAddress

-- | Validates whether a particular string is an email address
--   according to RFC5322.
isValid :: ByteString -> Bool
isValid :: ByteString -> Bool
isValid = (String -> Bool)
-> (EmailAddress -> Bool) -> Either String EmailAddress -> Bool
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Bool -> String -> Bool
forall a b. a -> b -> a
const Bool
False) (Bool -> EmailAddress -> Bool
forall a b. a -> b -> a
const Bool
True) (Either String EmailAddress -> Bool)
-> (ByteString -> Either String EmailAddress) -> ByteString -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String EmailAddress
validate

-- | If you want to find out *why* a particular string is not
--   an email address, use this.
--
-- Examples (both require `OverloadedStrings` to be enabled):
--
-- >>> validate "example@example.com"
-- Right "example@example.com"
--
-- >>> validate "not.good"
-- Left "at sign > @: not enough input"
validate :: ByteString -> Either String EmailAddress
validate :: ByteString -> Either String EmailAddress
validate = Parser EmailAddress -> ByteString -> Either String EmailAddress
forall a. Parser a -> ByteString -> Either String a
parseOnly (Parser EmailAddress
addrSpec Parser EmailAddress
-> (EmailAddress -> Parser EmailAddress) -> Parser EmailAddress
forall a b.
Parser ByteString a
-> (a -> Parser ByteString b) -> Parser ByteString b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \EmailAddress
r -> Parser ByteString ()
forall t. Chunk t => Parser t ()
endOfInput Parser ByteString () -> Parser EmailAddress -> Parser EmailAddress
forall a b.
Parser ByteString a -> Parser ByteString b -> Parser ByteString b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> EmailAddress -> Parser EmailAddress
forall a. a -> Parser ByteString a
forall (m :: * -> *) a. Monad m => a -> m a
return EmailAddress
r)