summaryrefslogtreecommitdiffstats
path: root/app/Data
diff options
context:
space:
mode:
authorRutger Broekhoff2025-03-18 15:29:27 +0100
committerRutger Broekhoff2025-03-18 15:31:11 +0100
commit86c8896ee69b068368b4ef9a4c3923285907c328 (patch)
treedc6e4f58a511c58e2910e9f7ea900165da7d47c6 /app/Data
downloadrdcapsis-86c8896ee69b068368b4ef9a4c3923285907c328.tar.gz
rdcapsis-86c8896ee69b068368b4ef9a4c3923285907c328.zip
Parsing ING statements (POC)
Diffstat (limited to 'app/Data')
-rw-r--r--app/Data/Iban.hs40
1 files changed, 40 insertions, 0 deletions
diff --git a/app/Data/Iban.hs b/app/Data/Iban.hs
new file mode 100644
index 0000000..a42e192
--- /dev/null
+++ b/app/Data/Iban.hs
@@ -0,0 +1,40 @@
1module Data.Iban (Iban, mkIban) where
2
3import Control.Applicative ((<|>))
4import Data.Attoparsec.Text as AP
5import Data.Char
6 ( digitToInt,
7 ord,
8 toUpper,
9 )
10import Data.Text qualified as T
11
12newtype Iban = Iban T.Text deriving (Show, Eq)
13
14mkIban :: T.Text -> Either String Iban
15mkIban t = validateIban t >> return (Iban t)
16
17validateIban :: T.Text -> Either String ()
18validateIban t = AP.parseOnly ibanP t
19 where
20 ibanP = do
21 countryCode <- AP.count 2 ibanLetter
22 checkDigits <- AP.count 2 ibanDigit
23 chars <- AP.many1 ibanChar
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 (" ++ T.unpack t ++ ")"
30 else fail "IBAN has more than 34 characters"
31 where
32 ibanChar = ibanDigit <|> ibanLetter
33 ibanDigit = toInteger . digitToInt <$> AP.digit
34 ibanLetter = letterToInt <$> AP.letter
35 letterToInt c = toInteger (ord (toUpper c) - ord 'A' + 10)
36 charsToInteger = foldl' (\acc d -> if d >= 10 then acc * 100 + d else acc * 10 + d) 0
37 ibanToInteger countryCode checkDigits chars =
38 charsToInteger chars * 1000000 + charsToInteger countryCode * 100 + charsToInteger checkDigits
39 valid countryCode checkDigits chars =
40 ibanToInteger countryCode checkDigits chars `mod` 97 == 1