summaryrefslogtreecommitdiffstats
path: root/app/Import/Ing/Shared.hs
blob: c70f225a17a9e461c667306813fbea412af8b1cc (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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