diff options
author | Rutger Broekhoff | 2025-08-25 19:48:19 +0200 |
---|---|---|
committer | Rutger Broekhoff | 2025-08-25 19:48:19 +0200 |
commit | 95d50b25c990e8c945ce2507b16ff3c8b039d286 (patch) | |
tree | c1ff4c7f9601c6980eed1a7235ba336c5c6f6106 /app/Data/Iban.hs | |
parent | 29b26dcbc1404925bbf12cddd66f7fcd3c57cfe7 (diff) | |
download | rdcapsis-95d50b25c990e8c945ce2507b16ff3c8b039d286.tar.gz rdcapsis-95d50b25c990e8c945ce2507b16ff3c8b039d286.zip |
OCaml
Diffstat (limited to 'app/Data/Iban.hs')
-rw-r--r-- | app/Data/Iban.hs | 48 |
1 files changed, 0 insertions, 48 deletions
diff --git a/app/Data/Iban.hs b/app/Data/Iban.hs deleted file mode 100644 index d9566b9..0000000 --- a/app/Data/Iban.hs +++ /dev/null | |||
@@ -1,48 +0,0 @@ | |||
1 | module Data.Iban (Iban, mkIban, toText) where | ||
2 | |||
3 | import Control.Applicative ((<|>)) | ||
4 | import Data.Attoparsec.Text as AP | ||
5 | import Data.Char | ||
6 | ( digitToInt, | ||
7 | isAscii, | ||
8 | isDigit, | ||
9 | ord, | ||
10 | toUpper, | ||
11 | ) | ||
12 | import Data.Text qualified as T | ||
13 | |||
14 | newtype Iban = Iban T.Text deriving (Show, Eq) | ||
15 | |||
16 | mkIban :: T.Text -> Either String Iban | ||
17 | mkIban t = validateIban t >> return (Iban t) | ||
18 | |||
19 | validateIban :: T.Text -> Either String () | ||
20 | validateIban = AP.parseOnly $ do | ||
21 | countryCode <- AP.count 2 AP.letter | ||
22 | checkDigits <- AP.count 2 AP.digit | ||
23 | chars <- AP.many1 (AP.letter <|> AP.digit) | ||
24 | endOfInput | ||
25 | if length chars < 30 | ||
26 | then | ||
27 | if valid countryCode checkDigits chars | ||
28 | then return () | ||
29 | else fail $ "IBAN checksum does not match (" ++ countryCode ++ checkDigits ++ chars ++ ")" | ||
30 | else fail "IBAN has more than 34 characters" | ||
31 | where | ||
32 | letterToInt c = ord (toUpper c) - ord 'A' + 10 | ||
33 | charsToInteger = | ||
34 | foldl' | ||
35 | ( \acc -> \case | ||
36 | d | ||
37 | | isDigit d -> acc * 10 + toInteger (digitToInt d) | ||
38 | | isAscii d -> acc * 100 + toInteger (letterToInt d) | ||
39 | | otherwise -> error "unreachable" | ||
40 | ) | ||
41 | 0 | ||
42 | ibanToInteger countryCode checkDigits chars = | ||
43 | charsToInteger chars * 1000000 + charsToInteger countryCode * 100 + charsToInteger checkDigits | ||
44 | valid countryCode checkDigits chars = | ||
45 | ibanToInteger countryCode checkDigits chars `mod` 97 == 1 | ||
46 | |||
47 | toText :: Iban -> T.Text | ||
48 | toText (Iban t) = t | ||