module Text.Html.Email.Validate
(
isValidEmail
, EmailAddress(..)
, emailToText
, parseEmail
, emailParser
) where
import Control.Applicative
import Control.Monad (when)
import Data.Text (Text, intercalate)
import qualified Data.Text as T
import Data.Attoparsec.Text
import Data.Monoid ((<>))
import qualified Text.Read as Read
import Data.Data (Data, Typeable)
import GHC.Generics (Generic)
import qualified Text.Show.Text as TS
data EmailAddress = EmailAddress { localPart :: Text
, domainPart :: Text
} deriving (Eq, Ord, Data, Typeable, Generic)
instance Show EmailAddress where
show = TS.toString . TS.showb
instance Read EmailAddress where
readListPrec = Read.readListPrecDefault
readPrec = Read.parens $ do
text <- Read.readPrec
either (const Read.pfail) return $ parseOnly emailParser text
instance TS.Show EmailAddress where
showb EmailAddress{..} = TS.fromText localPart <> TS.singleton '@' <> TS.fromText domainPart
emailToText :: EmailAddress -> Text
emailToText = TS.show
isValidEmail :: Text -> Bool
isValidEmail = either (const False) (const True) . parseEmail
parseEmail :: Text -> Either String EmailAddress
parseEmail = parseOnly emailParser
emailParser :: Parser EmailAddress
emailParser = EmailAddress <$> (local <* char '@')
<*> (domain <* endOfInput)
local :: Parser Text
local = takeWhile1 (inClass "A-Za-z0-9!#$%&'*+/=?^_`{|}~.-")
domain :: Parser Text
domain = intercalate "." <$> label `sepBy1` char '.'
label :: Parser Text
label = do
lbl <- intercalate "-" <$> takeWhile1 (inClass "A-Za-z0-9") `sepBy1` char '-'
when (T.length lbl > 63) $ fail "Label is too long"
return lbl