module Addy.Internal.Validation
( validateHostName,
validateDomainName,
validateLocalPart,
validateDisplayName,
validateLiteral,
validateAddressTag,
validateCommentContent,
validateEmailAddr,
)
where
import Addy.Internal.Char
import Addy.Internal.Types
import qualified Data.ByteString as ByteString
import qualified Data.Text as Text
import qualified Data.Text.ICU as ICU
import Validation
validateHostName :: Text -> Validation (NonEmpty Error) HostName
validateHostName :: Text -> Validation (NonEmpty Error) HostName
validateHostName Text
content =
let content' :: Text
content' = Text -> Text
Text.toLower (NormalizationMode -> Text -> Text
ICU.normalize NormalizationMode
ICU.NFC Text
content)
in Text -> HostName
HN Text
content'
HostName
-> Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) HostName
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ( Text -> Text -> Validation (NonEmpty Error) ()
validateNotPrefix Text
"-" Text
content'
Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) () -> Validation (NonEmpty Error) ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Text -> Text -> Validation (NonEmpty Error) ()
validateNotSuffix Text
"-" Text
content'
Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) () -> Validation (NonEmpty Error) ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Char -> Bool) -> Text -> Validation (NonEmpty Error) ()
validateAllowedChars Char -> Bool
atext Text
content'
Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) () -> Validation (NonEmpty Error) ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength Int
1 Int
63 Text
content'
)
validateDomainName :: Text -> Validation (NonEmpty Error) DomainName
validateDomainName :: Text -> Validation (NonEmpty Error) DomainName
validateDomainName Text
name =
[HostName] -> DomainName
fromHostList ([HostName] -> DomainName)
-> Validation (NonEmpty Error) [HostName]
-> Validation (NonEmpty Error) DomainName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength Int
1 Int
255 Text
name Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) [HostName]
-> Validation (NonEmpty Error) [HostName]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Validation (NonEmpty Error) [HostName]
validHostList)
where
validHostList :: Validation (NonEmpty Error) [HostName]
validHostList :: Validation (NonEmpty Error) [HostName]
validHostList =
(Text
-> Validation (NonEmpty Error) [HostName]
-> Validation (NonEmpty Error) [HostName])
-> Validation (NonEmpty Error) [HostName]
-> [Text]
-> Validation (NonEmpty Error) [HostName]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr
( \Text
h Validation (NonEmpty Error) [HostName]
hs -> (:) (HostName -> [HostName] -> [HostName])
-> Validation (NonEmpty Error) HostName
-> Validation (NonEmpty Error) ([HostName] -> [HostName])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Validation (NonEmpty Error) HostName
validateHostName Text
h Validation (NonEmpty Error) ([HostName] -> [HostName])
-> Validation (NonEmpty Error) [HostName]
-> Validation (NonEmpty Error) [HostName]
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Validation (NonEmpty Error) [HostName]
hs
)
([HostName] -> Validation (NonEmpty Error) [HostName]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [])
(Text -> Text -> [Text]
Text.splitOn Text
"." Text
name)
fromHostList :: [HostName] -> DomainName
fromHostList :: [HostName] -> DomainName
fromHostList [HostName]
hs =
(HostName -> Text) -> [HostName] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map HostName -> Text
coerce [HostName]
hs
[Text] -> ([Text] -> Text) -> Text
forall a b. a -> (a -> b) -> b
& Text -> [Text] -> Text
Text.intercalate Text
"."
Text -> (Text -> DomainName) -> DomainName
forall a b. a -> (a -> b) -> b
& Text -> DomainName
DN
validateLocalPart ::
Text -> Validation (NonEmpty Error) LocalPart
validateLocalPart :: Text -> Validation (NonEmpty Error) LocalPart
validateLocalPart Text
content =
let content' :: Text
content' = NormalizationMode -> Text -> Text
ICU.normalize NormalizationMode
ICU.NFC Text
content
in Text -> LocalPart
LP Text
content'
LocalPart
-> Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) LocalPart
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ( Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength Int
1 Int
64 Text
content'
Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) () -> Validation (NonEmpty Error) ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Char -> Bool) -> Text -> Validation (NonEmpty Error) ()
validateAllowedChars Char -> Bool
allowedChar Text
content'
)
where
allowedChar :: Char -> Bool
allowedChar :: Char -> Bool
allowedChar Char
c = Char -> Bool
atext Char
c Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.' Bool -> Bool -> Bool
|| Char -> Bool
qtext Char
c Bool -> Bool -> Bool
|| Char -> Bool
quotedPair Char
c
validateDisplayName :: Text -> Validation (NonEmpty Error) DisplayName
validateDisplayName :: Text -> Validation (NonEmpty Error) DisplayName
validateDisplayName Text
content =
Text -> DisplayName
DP Text
content
DisplayName
-> Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) DisplayName
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ( Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength Int
1 Int
64 Text
content
Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) () -> Validation (NonEmpty Error) ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Char -> Bool) -> Text -> Validation (NonEmpty Error) ()
validateAllowedChars Char -> Bool
allowedChar Text
content
)
where
allowedChar :: Char -> Bool
allowedChar :: Char -> Bool
allowedChar Char
c = Char -> Bool
atext Char
c Bool -> Bool -> Bool
|| Char -> Bool
qtext Char
c Bool -> Bool -> Bool
|| Char -> Bool
quotedPair Char
c
validateLiteral :: Text -> Validation (NonEmpty Error) Literal
validateLiteral :: Text -> Validation (NonEmpty Error) Literal
validateLiteral Text
content =
Text -> Literal
Lit Text
content
Literal
-> Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) Literal
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ( Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength Int
1 Int
63 Text
content
Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) () -> Validation (NonEmpty Error) ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Char -> Bool) -> Text -> Validation (NonEmpty Error) ()
validateAllowedChars Char -> Bool
allowedChar Text
content
)
where
allowedChar :: Char -> Bool
allowedChar :: Char -> Bool
allowedChar Char
c = Char -> Bool
dtext Char
c Bool -> Bool -> Bool
|| Char -> Bool
wsp Char
c Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\r' Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n'
validateAddressTag :: Text -> Validation (NonEmpty Error) AddressTag
validateAddressTag :: Text -> Validation (NonEmpty Error) AddressTag
validateAddressTag Text
content = Text -> AddressTag
AT Text
content AddressTag
-> Validation (NonEmpty Error) Literal
-> Validation (NonEmpty Error) AddressTag
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Text -> Validation (NonEmpty Error) Literal
validateLiteral Text
content
validateCommentContent :: Text -> Validation (NonEmpty Error) CommentContent
Text
content =
Text -> CommentContent
CC Text
content
CommentContent
-> Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) CommentContent
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ( Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength Int
1 Int
64 Text
content
Validation (NonEmpty Error) ()
-> Validation (NonEmpty Error) () -> Validation (NonEmpty Error) ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Char -> Bool) -> Text -> Validation (NonEmpty Error) ()
validateAllowedChars Char -> Bool
allowedChar Text
content
)
where
allowedChar :: Char -> Bool
allowedChar :: Char -> Bool
allowedChar Char
c = Char -> Bool
ctext Char
c Bool -> Bool -> Bool
|| Char -> Bool
quotedPair Char
c
validateEmailAddr :: EmailAddr -> Validation (NonEmpty Error) EmailAddr
validateEmailAddr :: EmailAddr -> Validation (NonEmpty Error) EmailAddr
validateEmailAddr EmailAddr {[Comment]
Maybe DisplayName
Domain
LocalPart
_comments :: EmailAddr -> [Comment]
_domain :: EmailAddr -> Domain
_localPart :: EmailAddr -> LocalPart
_displayName :: EmailAddr -> Maybe DisplayName
_comments :: [Comment]
_domain :: Domain
_localPart :: LocalPart
_displayName :: Maybe DisplayName
..} =
Maybe DisplayName -> LocalPart -> Domain -> [Comment] -> EmailAddr
EmailAddr
(Maybe DisplayName
-> LocalPart -> Domain -> [Comment] -> EmailAddr)
-> Validation (NonEmpty Error) (Maybe DisplayName)
-> Validation
(NonEmpty Error) (LocalPart -> Domain -> [Comment] -> EmailAddr)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Validation (NonEmpty Error) (Maybe DisplayName)
displayNameV
Validation
(NonEmpty Error) (LocalPart -> Domain -> [Comment] -> EmailAddr)
-> Validation (NonEmpty Error) LocalPart
-> Validation (NonEmpty Error) (Domain -> [Comment] -> EmailAddr)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Validation (NonEmpty Error) LocalPart
validateLocalPart (LocalPart -> Text
localPartText LocalPart
_localPart)
Validation (NonEmpty Error) (Domain -> [Comment] -> EmailAddr)
-> Validation (NonEmpty Error) Domain
-> Validation (NonEmpty Error) ([Comment] -> EmailAddr)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Validation (NonEmpty Error) Domain
domainV
Validation (NonEmpty Error) ([Comment] -> EmailAddr)
-> Validation (NonEmpty Error) [Comment]
-> Validation (NonEmpty Error) EmailAddr
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Validation (NonEmpty Error) [Comment]
commentsV
where
displayNameV :: Validation (NonEmpty Error) (Maybe DisplayName)
displayNameV :: Validation (NonEmpty Error) (Maybe DisplayName)
displayNameV = case Maybe DisplayName
_displayName of
Maybe DisplayName
Nothing -> Maybe DisplayName
-> Validation (NonEmpty Error) (Maybe DisplayName)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe DisplayName
forall a. Maybe a
Nothing
Just (DP Text
t) -> DisplayName -> Maybe DisplayName
forall a. a -> Maybe a
Just (DisplayName -> Maybe DisplayName)
-> Validation (NonEmpty Error) DisplayName
-> Validation (NonEmpty Error) (Maybe DisplayName)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Validation (NonEmpty Error) DisplayName
validateDisplayName Text
t
domainV :: Validation (NonEmpty Error) Domain
domainV :: Validation (NonEmpty Error) Domain
domainV = case Domain
_domain of
Domain (DN Text
t) -> DomainName -> Domain
Domain (DomainName -> Domain)
-> Validation (NonEmpty Error) DomainName
-> Validation (NonEmpty Error) Domain
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Validation (NonEmpty Error) DomainName
validateDomainName Text
t
DomainLiteral AddressLiteral
lit -> AddressLiteral -> Domain
DomainLiteral (AddressLiteral -> Domain)
-> Validation (NonEmpty Error) AddressLiteral
-> Validation (NonEmpty Error) Domain
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> AddressLiteral -> Validation (NonEmpty Error) AddressLiteral
addrLiteralV AddressLiteral
lit
addrLiteralV :: AddressLiteral -> Validation (NonEmpty Error) AddressLiteral
addrLiteralV :: AddressLiteral -> Validation (NonEmpty Error) AddressLiteral
addrLiteralV = \case
IpAddressLiteral IP
ip ->
AddressLiteral -> Validation (NonEmpty Error) AddressLiteral
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IP -> AddressLiteral
IpAddressLiteral IP
ip)
TaggedAddressLiteral (AT Text
at) (Lit Text
lit) ->
AddressTag -> Literal -> AddressLiteral
TaggedAddressLiteral (AddressTag -> Literal -> AddressLiteral)
-> Validation (NonEmpty Error) AddressTag
-> Validation (NonEmpty Error) (Literal -> AddressLiteral)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Validation (NonEmpty Error) AddressTag
validateAddressTag Text
at Validation (NonEmpty Error) (Literal -> AddressLiteral)
-> Validation (NonEmpty Error) Literal
-> Validation (NonEmpty Error) AddressLiteral
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Validation (NonEmpty Error) Literal
validateLiteral Text
lit
AddressLiteral (Lit Text
t) ->
Literal -> AddressLiteral
AddressLiteral (Literal -> AddressLiteral)
-> Validation (NonEmpty Error) Literal
-> Validation (NonEmpty Error) AddressLiteral
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Validation (NonEmpty Error) Literal
validateLiteral Text
t
commentsV :: Validation (NonEmpty Error) [Comment]
commentsV :: Validation (NonEmpty Error) [Comment]
commentsV =
(Comment
-> Validation (NonEmpty Error) [Comment]
-> Validation (NonEmpty Error) [Comment])
-> Validation (NonEmpty Error) [Comment]
-> [Comment]
-> Validation (NonEmpty Error) [Comment]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr
( \(Comment CommentLoc
loc (CC Text
t)) Validation (NonEmpty Error) [Comment]
cs ->
(:) (Comment -> [Comment] -> [Comment])
-> (CommentContent -> Comment)
-> CommentContent
-> [Comment]
-> [Comment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommentLoc -> CommentContent -> Comment
Comment CommentLoc
loc (CommentContent -> [Comment] -> [Comment])
-> Validation (NonEmpty Error) CommentContent
-> Validation (NonEmpty Error) ([Comment] -> [Comment])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Validation (NonEmpty Error) CommentContent
validateCommentContent Text
t Validation (NonEmpty Error) ([Comment] -> [Comment])
-> Validation (NonEmpty Error) [Comment]
-> Validation (NonEmpty Error) [Comment]
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Validation (NonEmpty Error) [Comment]
cs
)
([Comment] -> Validation (NonEmpty Error) [Comment]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [])
[Comment]
_comments
validateNotPrefix :: Text -> Text -> Validation (NonEmpty Error) ()
validateNotPrefix :: Text -> Text -> Validation (NonEmpty Error) ()
validateNotPrefix Text
prefix Text
name =
Bool -> Error -> Validation (NonEmpty Error) ()
forall e. Bool -> e -> Validation (NonEmpty e) ()
failureIf (Text -> Text -> Bool
Text.isPrefixOf Text
prefix Text
name) (Text -> Error
InvalidPrefixError Text
prefix)
validateNotSuffix :: Text -> Text -> Validation (NonEmpty Error) ()
validateNotSuffix :: Text -> Text -> Validation (NonEmpty Error) ()
validateNotSuffix Text
suffix Text
name =
Bool -> Error -> Validation (NonEmpty Error) ()
forall e. Bool -> e -> Validation (NonEmpty e) ()
failureIf (Text -> Text -> Bool
Text.isSuffixOf Text
suffix Text
name) (Text -> Error
InvalidSuffixError Text
suffix)
validateAllowedChars :: (Char -> Bool) -> Text -> Validation (NonEmpty Error) ()
validateAllowedChars :: (Char -> Bool) -> Text -> Validation (NonEmpty Error) ()
validateAllowedChars Char -> Bool
f Text
t =
Bool -> Error -> Validation (NonEmpty Error) ()
forall e. Bool -> e -> Validation (NonEmpty e) ()
failureUnless ((Char -> Bool) -> Text -> Bool
Text.all Char -> Bool
f Text
t) (Text -> Error
InvalidCharactersError (Text -> Error) -> Text -> Error
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> Text -> Text
Text.filter (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
f) Text
t)
validateLength :: Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength :: Int -> Int -> Text -> Validation (NonEmpty Error) ()
validateLength Int
minL Int
maxL Text
t =
let bytes :: Int
bytes = ByteString -> Int
ByteString.length (Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 Text
t)
in Bool -> Error -> Validation (NonEmpty Error) ()
forall e. Bool -> e -> Validation (NonEmpty e) ()
failureIf
(Int
bytes Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
minL Bool -> Bool -> Bool
|| Int
bytes Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
maxL)
(Int -> Int -> Int -> Error
InvalidLengthError Int
minL Int
maxL Int
bytes)