zxcvbn-hs-0.2.0.0: src/Text/Password/Strength/Internal/Token.hs
{-# LANGUAGE TemplateHaskell #-}
{-|
Copyright:
This file is part of the package zxcvbn-hs. It is subject to the
license terms in the LICENSE file found in the top-level directory
of this distribution and at:
https://code.devalot.com/sthenauth/zxcvbn-hs
No part of this package, including this file, may be copied,
modified, propagated, or distributed except according to the terms
contained in the LICENSE file.
License: MIT
-}
module Text.Password.Strength.Internal.Token (
-- * Splitting a Password into Tokens
Token(..),
allTokens,
-- * Lenses for the 'Token' Type
tokenChars,
tokenLower,
startIndex,
endIndex,
-- * Translate the Characters of a Password
translateMap
) where
--------------------------------------------------------------------------------
-- Library Imports:
import Control.Lens.TH (makeLenses)
import Data.Text (Text)
import qualified Data.Text as Text
--------------------------------------------------------------------------------
-- | A token is a substring of a password.
data Token = Token
{ _tokenChars :: Text
, _tokenLower :: Text
, _startIndex :: Int
, _endIndex :: Int
} deriving (Show, Eq, Ord)
makeLenses ''Token
--------------------------------------------------------------------------------
-- | Extract all substrings from the input 'Text'. A substring has a
-- minimum character length of 3 for performance and to prevent false
-- positives for matches such as sequences and repeats.
--
-- Examples:
--
-- >>> map _tokenChars (allTokens "abcdef")
-- ["abc","abcd","abcde","abcdef","bcd","bcde","bcdef","cde","cdef","def"]
allTokens :: Text -> [Token]
allTokens = outer 0
where
outer :: Int -> Text -> [Token]
outer i t
| Text.null t = [ ]
| otherwise = inner i 2 t ++ outer (i+1) (Text.drop 1 t)
inner :: Int -> Int -> Text -> [Token]
inner i j t
| Text.compareLength t (j+1) == LT = [ ]
| otherwise = mkT i j t : inner i (j+1) t
mkT :: Int -> Int -> Text -> Token
mkT i j t =
let chars = Text.take (j+1) t
in Token chars (Text.toLower chars) i (i + j)
--------------------------------------------------------------------------------
-- | Translate the characters of a 'Text' value.
--
-- Given a function that translates a character into one or more
-- characters, return all possible translations.
--
-- Examples:
--
-- >>> translateMap l33t2Eng "p111
-- ["piii","plii","pili","plli","piil","plil","pill","plll"]
translateMap :: (Char -> String) -> Text -> [Text]
translateMap f = map Text.pack . Text.foldr fork [""]
where
fork :: Char -> [String] -> [String]
fork c cs =
case f c of
[] -- No translations so keep the existing char:
-> map (c:) cs
xs -- Add (length xs) new forks of the text:
-> concatMap (\c' -> map (c':) cs) xs