From e40e290ef216656d304f4f3095dbef223e94191d Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Fri, 21 Mar 2025 09:45:22 +0100 Subject: Changes --- app/Data/Iban.hs | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'app/Data') diff --git a/app/Data/Iban.hs b/app/Data/Iban.hs index a42e192..45343ec 100644 --- a/app/Data/Iban.hs +++ b/app/Data/Iban.hs @@ -15,26 +15,31 @@ mkIban :: T.Text -> Either String Iban mkIban t = validateIban t >> return (Iban t) validateIban :: T.Text -> Either String () -validateIban t = AP.parseOnly ibanP t +validateIban = AP.parseOnly $ do + countryCode <- AP.count 2 AP.letter + checkDigits <- AP.count 2 AP.digit + chars <- AP.many1 (AP.letter <|> AP.digit) + endOfInput + if length chars < 30 + then + if valid countryCode checkDigits chars + then return () + else fail $ "IBAN checksum does not match (" <> countryCode <> checkDigits <> chars <> ")" + else fail "IBAN has more than 34 characters" where - ibanP = do - countryCode <- AP.count 2 ibanLetter - checkDigits <- AP.count 2 ibanDigit - chars <- AP.many1 ibanChar - endOfInput - if length chars < 30 - then - if valid countryCode checkDigits chars - then return () - else fail $ "IBAN checksum does not match (" ++ T.unpack t ++ ")" - else fail "IBAN has more than 34 characters" - where - ibanChar = ibanDigit <|> ibanLetter - ibanDigit = toInteger . digitToInt <$> AP.digit - ibanLetter = letterToInt <$> AP.letter - letterToInt c = toInteger (ord (toUpper c) - ord 'A' + 10) - charsToInteger = foldl' (\acc d -> if d >= 10 then acc * 100 + d else acc * 10 + d) 0 - ibanToInteger countryCode checkDigits chars = - charsToInteger chars * 1000000 + charsToInteger countryCode * 100 + charsToInteger checkDigits - valid countryCode checkDigits chars = - ibanToInteger countryCode checkDigits chars `mod` 97 == 1 + letterToInt c = ord (toUpper c) - ord 'A' + 10 + charsToInteger = + foldl' + ( \acc -> \case + d + | '0' <= d && d <= '9' -> acc * 10 + toInteger (digitToInt d) + | 'A' <= d && d <= 'Z' + || 'a' <= d && d <= 'z' -> + acc * 100 + toInteger (letterToInt d) + | otherwise -> error "unreachable" + ) + 0 + ibanToInteger countryCode checkDigits chars = + charsToInteger chars * 1000000 + charsToInteger countryCode * 100 + charsToInteger checkDigits + valid countryCode checkDigits chars = + ibanToInteger countryCode checkDigits chars `mod` 97 == 1 -- cgit v1.2.3