Credit cards

Summary:
Add the ability to parse credit cards and identify the issuer in Duckling.

Credit card numbers are a sequence of 8 to 19 digits, with a few specific digits at the start that identify the card issuer. They also satisfy Luhn checksum (https://en.wikipedia.org/wiki/Luhn_algorithm).

Reviewed By: chinmay87

Differential Revision: D13014623

fbshipit-source-id: 96586e074777ae90a4a39c515648c31a8111f0c2
This commit is contained in:
Giovanni Cappellotto 2018-11-14 11:00:58 -08:00 committed by Facebook Github Bot
parent 4c4e0c59b7
commit 1052914cb4
50 changed files with 381 additions and 0 deletions

View File

@ -0,0 +1,57 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE OverloadedStrings #-}
module Duckling.CreditCardNumber.Corpus
( corpus
, negativeCorpus
) where
import Prelude
import Data.String
import qualified Data.Text as T
import Duckling.CreditCardNumber.Types
import Duckling.CreditCardNumber.Helpers
import Duckling.Testing.Types
corpus :: Corpus
corpus = (testContext, testOptions, allExamples)
negativeCorpus :: NegativeCorpus
negativeCorpus = (testContext, testOptions, examples)
where
examples =
[ T.replicate (minNumberDigits - 1) "0"
, T.replicate (maxNumberDigits + 1) "0"
, "invalid"
, "4111111111111110"
]
allExamples :: [Example]
allExamples = concat
[ examples
(CreditCardNumberValue "4111111111111111" Visa)
[ "4111111111111111" ]
, examples
(CreditCardNumberValue "371449635398431" Amex)
[ "371449635398431" ]
, examples
(CreditCardNumberValue "6011111111111117" Discover)
[ "6011111111111117" ]
, examples
(CreditCardNumberValue "5555555555554444" Mastercard)
[ "5555555555554444" ]
, examples
(CreditCardNumberValue "30569309025904" DinerClub)
[ "30569309025904" ]
, examples
(CreditCardNumberValue "3530111333300000" Other)
[ "3530111333300000" ]
]

View File

@ -0,0 +1,108 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
module Duckling.CreditCardNumber.Helpers
( otherCreditCardNumberRegex
, visaCreditCardNumberRegex
, amexCreditCardNumberRegex
, discoverCreditCardNumberRegex
, mastercardCreditCardNumberRegex
, dinerClubCreditCardNumberRegex
, isValidCreditCardNumber
, minNumberDigits
, maxNumberDigits
, creditCard
) where
import Data.Text (Text)
import Prelude
import Data.String
import Duckling.CreditCardNumber.Types (CreditCardNumberData(..))
import qualified Duckling.CreditCardNumber.Types as TCreditCardNumber
import qualified Data.Text as T
import qualified Data.Char as C
import qualified Data.Bits as B
-- -----------------------------------------------------------------
-- Patterns
otherCreditCardNumberRegex :: String
otherCreditCardNumberRegex =
concat [ "("
, "(?!" , visaCreditCardNumberRegex, ")"
, "(?!" , amexCreditCardNumberRegex, ")"
, "(?!" , discoverCreditCardNumberRegex, ")"
, "(?!" , mastercardCreditCardNumberRegex, ")"
, "(?!" , dinerClubCreditCardNumberRegex, ")"
, "\\d{" , show minNumberDigits , "," , show maxNumberDigits , "}"
, ")"
]
-- | Visa credit card regex informed by latest BIN info
visaCreditCardNumberRegex :: String
visaCreditCardNumberRegex = "(4[0-9]{12}(?:[0-9]{3}))"
-- | American Express credit card regex informed by latest BIN info
amexCreditCardNumberRegex :: String
amexCreditCardNumberRegex = "(3[47][0-9]{13})"
-- | Discover credit card regex informed by latest BIN info
discoverCreditCardNumberRegex :: String
discoverCreditCardNumberRegex = "(6(?:011|5[0-9]{2})[0-9]{12})"
-- | Mastercard credit card regex informed by latest BIN info
mastercardCreditCardNumberRegex :: String
mastercardCreditCardNumberRegex = "(5[1-5][0-9]{14})"
-- | Diner Club credit card regex informed by latest BIN info
dinerClubCreditCardNumberRegex :: String
dinerClubCreditCardNumberRegex = "(3(?:0[0-5]|[68][0-9])[0-9]{11})"
-- -----------------------------------------------------------------
-- Validation
-- | An implementation of the Luhn algorithm (see
-- https://en.wikipedia.org/wiki/Luhn_algorithm) to check if a given credit card
-- number is valid
isValidCreditCardNumber :: Text -> Bool
isValidCreditCardNumber ccNum =
T.length ccNum >= minNumberDigits &&
T.length ccNum <= maxNumberDigits &&
validCheckSum
where
validCheckSum :: Bool
validCheckSum =
T.all C.isDigit ccNum &&
fst (T.foldr f (0, 0) ccNum) `rem` 10 == 0
where
f char (checksum, e) =
let
val = C.digitToInt char
-- every even digit should be doubled
d = sumDigits (B.shift val e)
in (checksum + d, 1 - e)
-- we only need sum of digits for numbers from 0 to 18
sumDigits a
| a > 9 = a - 9
| otherwise = a
minNumberDigits :: Int
minNumberDigits = 8
maxNumberDigits :: Int
maxNumberDigits = 19
-- -----------------------------------------------------------------
-- Production
creditCard :: Text -> TCreditCardNumber.Issuer -> CreditCardNumberData
creditCard ccNum i =
CreditCardNumberData { TCreditCardNumber.number = ccNum
, TCreditCardNumber.issuer = i
}

View File

@ -0,0 +1,69 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.CreditCardNumber.Rules
( rules ) where
import Prelude
import Data.String
import Data.Text (Text)
import Data.Bool
import Duckling.Dimensions.Types
import Duckling.CreditCardNumber.Helpers
import qualified Duckling.CreditCardNumber.Types as TCreditCardNumber
import Duckling.Regex.Types
import Duckling.Types
creditCards :: [(Text, PatternItem, TCreditCardNumber.Issuer)]
creditCards =
[ ( "visa credit card number"
, regex visaCreditCardNumberRegex
, TCreditCardNumber.Visa
)
, ( "amex card number"
, regex amexCreditCardNumberRegex
, TCreditCardNumber.Amex
)
, ( "discover card number"
, regex discoverCreditCardNumberRegex
, TCreditCardNumber.Discover
)
, ( "mastercard card number"
, regex mastercardCreditCardNumberRegex
, TCreditCardNumber.Mastercard
)
, ( "diner club card number"
, regex dinerClubCreditCardNumberRegex
, TCreditCardNumber.DinerClub
)
, ( "credit card number"
, regex otherCreditCardNumberRegex
, TCreditCardNumber.Other
)
]
rules :: [Rule]
rules = map go creditCards
where
go :: (Text, PatternItem, TCreditCardNumber.Issuer) -> Rule
go (name, regexPattern, i) = Rule
{ name = name
, pattern = [ regexPattern ]
, prod = \case
(Token RegexMatch (GroupMatch (ccNum:_)):_) ->
bool
Nothing
(Just $ Token CreditCardNumber $ creditCard ccNum i)
(isValidCreditCardNumber ccNum)
_ -> Nothing
}

View File

@ -0,0 +1,62 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
module Duckling.CreditCardNumber.Types where
import Control.DeepSeq
import Data.Aeson
import Data.Hashable
import Data.Text (Text)
import qualified Data.Text as Text
import GHC.Generics
import Prelude
import Duckling.Resolve (Resolve(..))
data Issuer
= Visa
| Amex
| Discover
| Mastercard
| DinerClub
| Other
deriving (Eq, Generic, Hashable, Ord, Show, NFData)
instance ToJSON Issuer where
toJSON = String . Text.toLower . Text.pack . show
data CreditCardNumberData = CreditCardNumberData
{ number :: Text
, issuer :: Issuer
}
deriving (Eq, Generic, Hashable, Ord, Show, NFData)
instance Resolve CreditCardNumberData where
type ResolvedValue CreditCardNumberData = CreditCardNumberValue
resolve _ _ CreditCardNumberData {number, issuer} =
Just (CreditCardNumberValue number issuer, False)
data CreditCardNumberValue = CreditCardNumberValue
{ vNumber :: Text
, vIssuer :: Issuer
}
deriving (Eq, Ord, Show)
instance ToJSON CreditCardNumberValue where
toJSON (CreditCardNumberValue number issuer) =
object [ "value" .= number
, "issuer" .= issuer
]

View File

@ -74,6 +74,7 @@ explicitDimensions targets = HashSet.union targets deps
-- | Ordinal depends on Numeral for JA, KO, and ZH.
dependents :: Some Dimension -> HashSet (Some Dimension)
dependents (This CreditCardNumber) = HashSet.empty
dependents (This Distance) = HashSet.singleton (This Numeral)
dependents (This Duration) = HashSet.fromList [This Numeral, This TimeGrain]
dependents (This Numeral) = HashSet.empty

View File

@ -29,6 +29,7 @@ import Duckling.Types
toName :: Dimension a -> Text
toName RegexMatch = "regex"
toName CreditCardNumber = "credit-card-number"
toName Distance = "distance"
toName Duration = "duration"
toName Email = "email"

View File

@ -37,6 +37,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -34,6 +34,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -28,6 +28,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Numeral) = Numeral.rules

View File

@ -30,6 +30,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -16,6 +16,7 @@ module Duckling.Rules.Common
import Duckling.Dimensions.Types
import Duckling.Types
import qualified Duckling.AmountOfMoney.Rules as AmountOfMoney
import qualified Duckling.CreditCardNumber.Rules as CreditCardNumber
import qualified Duckling.Distance.Rules as Distance
import qualified Duckling.Duration.Rules as Duration
import qualified Duckling.Email.Rules as Email
@ -27,6 +28,7 @@ import qualified Duckling.Volume.Rules as Volume
rules :: Some Dimension -> [Rule]
rules (This AmountOfMoney) = AmountOfMoney.rules
rules (This CreditCardNumber) = CreditCardNumber.rules
rules (This Distance) = Distance.rules
rules (This Duration) = Duration.rules
rules (This Email) = Email.rules

View File

@ -33,6 +33,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -34,6 +34,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = Email.rules

View File

@ -33,6 +33,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -90,6 +90,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = Email.rules

View File

@ -36,6 +36,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -30,6 +30,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -28,6 +28,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -39,6 +39,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = Email.rules

View File

@ -37,6 +37,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -33,6 +33,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -32,6 +32,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Numeral) = Numeral.rules

View File

@ -39,6 +39,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -33,6 +33,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -31,6 +31,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -28,6 +28,7 @@ localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Numeral) = Numeral.rules

View File

@ -36,6 +36,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = Email.rules

View File

@ -32,6 +32,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -34,6 +34,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -34,6 +34,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -38,6 +38,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -29,6 +29,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -30,6 +30,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -29,6 +29,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -34,6 +34,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -28,6 +28,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Numeral) = Numeral.rules

View File

@ -41,6 +41,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -33,6 +33,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -38,6 +38,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -38,6 +38,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -36,6 +36,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -35,6 +35,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -29,6 +29,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Numeral) = Numeral.rules

View File

@ -35,6 +35,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = Duration.rules
langRules (This Email) = []

View File

@ -30,6 +30,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = []
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -33,6 +33,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = []
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -44,6 +44,7 @@ localeRules _ _ = []
langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
langRules (This CreditCardNumber) = []
langRules (This Distance) = Distance.rules
langRules (This Duration) = []
langRules (This Email) = []

View File

@ -42,6 +42,7 @@ import qualified Text.Regex.PCRE as PCRE
import qualified TextShow as TS
import Duckling.AmountOfMoney.Types (AmountOfMoneyData)
import Duckling.CreditCardNumber.Types (CreditCardNumberData)
import Duckling.Distance.Types (DistanceData)
import Duckling.Duration.Types (DurationData)
import Duckling.Email.Types (EmailData)
@ -93,6 +94,7 @@ class (Show a, Typeable a, Typeable (DimensionData a)) =>
data Dimension a where
RegexMatch :: Dimension GroupMatch
AmountOfMoney :: Dimension AmountOfMoneyData
CreditCardNumber :: Dimension CreditCardNumberData
Distance :: Dimension DistanceData
Duration :: Dimension DurationData
Email :: Dimension EmailData
@ -110,6 +112,7 @@ data Dimension a where
-- Show
instance Show (Dimension a) where
show RegexMatch = "RegexMatch"
show CreditCardNumber = "CreditCardNumber"
show Distance = "Distance"
show Duration = "Duration"
show Email = "Email"
@ -151,10 +154,13 @@ instance Hashable (Dimension a) where
hashWithSalt s Url = hashWithSalt s (12::Int)
hashWithSalt s Volume = hashWithSalt s (13::Int)
hashWithSalt s (CustomDimension _) = hashWithSalt s (14::Int)
hashWithSalt s CreditCardNumber = hashWithSalt s (15::Int)
instance GEq Dimension where
geq RegexMatch RegexMatch = Just Refl
geq RegexMatch _ = Nothing
geq CreditCardNumber CreditCardNumber = Just Refl
geq CreditCardNumber _ = Nothing
geq Distance Distance = Just Refl
geq Distance _ = Nothing
geq Duration Duration = Just Refl

View File

@ -736,6 +736,12 @@ library
, Duckling.Volume.Helpers
, Duckling.Volume.Rules
, Duckling.Volume.Types
-- CreditCardNumber
, Duckling.CreditCardNumber.Corpus
, Duckling.CreditCardNumber.Helpers
, Duckling.CreditCardNumber.Rules
, Duckling.CreditCardNumber.Types
build-depends: base >= 4.8.2 && < 5.0
, array >= 0.5.1.1 && < 0.6
, attoparsec >= 0.13.1.0 && < 0.14
@ -1006,6 +1012,9 @@ test-suite duckling-test
, Duckling.Volume.TR.Tests
, Duckling.Volume.Tests
-- CreditCardNumber
, Duckling.CreditCardNumber.Tests
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wincomplete-patterns
default-language: Haskell2010
default-extensions: OverloadedStrings

View File

@ -0,0 +1,26 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE OverloadedStrings #-}
module Duckling.CreditCardNumber.Tests
( tests
) where
import Data.String
import Test.Tasty
import Duckling.Dimensions.Types
import Duckling.CreditCardNumber.Corpus
import Duckling.Testing.Asserts
tests :: TestTree
tests = testGroup "CreditCardNumber Tests"
[ makeCorpusTest [This CreditCardNumber] corpus
, makeNegativeCorpusTest [This CreditCardNumber] negativeCorpus
]