module Import.Ing.Shared where import Data.Attoparsec.Text qualified as AP import Data.Char (digitToInt, ord) import Data.Csv qualified as C import Data.Decimal (Decimal, DecimalRaw (Decimal), normalizeDecimal) import Data.Iban (Iban, mkIban) import Data.Text qualified as T import Data.Time.Calendar (Day) import Data.Time.Clock (UTCTime) import Data.Time.Format (defaultTimeLocale, parseTimeM) import Data.Time.Zones (TZ, localTimeToUTCTZ) data DebitCredit = Debit | Credit deriving (Show) readDecimal :: T.Text -> Either String Decimal readDecimal = AP.parseOnly $ do decPart <- AP.decimal _ <- AP.char ',' f1 <- AP.digit f2 <- AP.digit AP.endOfInput let fracPart = fromIntegral $ digitToInt f1 * 10 + digitToInt f2 return $ normalizeDecimal (Decimal 2 (decPart * 100 + fracPart)) scsvOptions :: C.DecodeOptions scsvOptions = C.defaultDecodeOptions {C.decDelimiter = fromIntegral (ord ';')} eitherToCP :: Either String a -> C.Parser a eitherToCP = either fail return decimalCP :: T.Text -> C.Parser Decimal decimalCP = eitherToCP . readDecimal dateCP :: String -> T.Text -> C.Parser Day dateCP fmt = parseTimeM False defaultTimeLocale fmt . T.unpack maybeCP :: (T.Text -> C.Parser a) -> T.Text -> C.Parser (Maybe a) maybeCP p t = if T.null t then return Nothing else Just <$> p t ibanCP :: T.Text -> C.Parser Iban ibanCP = eitherToCP . mkIban timestampCP :: String -> TZ -> T.Text -> C.Parser UTCTime timestampCP fmt amsTz t = do localTime <- parseTimeM False defaultTimeLocale fmt (T.unpack t) return $ localTimeToUTCTZ amsTz localTime