Compare commits

..

No commits in common. "f9c1e36ee8985f15fc6fe62ff831fdf60f6b5b1f" and "0faea1161c60da3e9b845c27ec08c54c32298564" have entirely different histories.

6 changed files with 173 additions and 182 deletions

View File

@ -157,12 +157,17 @@ runDumpAccountKeys c = do
runSync :: MonadUnliftIO m => FilePath -> m () runSync :: MonadUnliftIO m => FilePath -> m ()
runSync c = do runSync c = do
config <- readConfig c config <- readConfig c
migrate_ (sqlConfig config) $ do catch (sync_ config) $ \case
s <- getDBState config InsertException _ -> liftIO $ putStrLn "insert error"
flip runReaderT (s $ takeDirectory c) $ do where
es1 <- insertBudget $ budget config sync_ config = migrate_ (sqlConfig config) $ do
es2 <- insertStatements config res <- getDBState config
let es = es1 ++ es2 case res of
unless (null es) $ throwIO $ InsertException es Left es -> throwIO $ InsertException es
Right s -> flip runReaderT (s $ takeDirectory c) $ do
es1 <- insertBudget $ budget config
es2 <- insertStatements config
let es = es1 ++ es2
unless (null es) $ throwIO $ InsertException es
-- showBalances -- showBalances

View File

@ -306,19 +306,23 @@ indexAcntRoot r =
getDBState getDBState
:: MonadUnliftIO m :: MonadUnliftIO m
=> Config => Config
-> SqlPersistT m (FilePath -> DBState) -> SqlPersistT m (EitherErrs (FilePath -> DBState))
getDBState c = do getDBState c = mapM (uncurry go) $ concatEithers2 bi si (,)
am <- updateAccounts $ accounts c where
cm <- updateCurrencies $ currencies c bi = intervalMaybeBounds $ budgetInterval $ global c
hs <- updateHashes c si = intervalMaybeBounds $ statementInterval $ global c
-- TODO not sure how I feel about this, probably will change this struct alot go budgetInt statementInt = do
-- in the future so whatever...for now am <- updateAccounts $ accounts c
return $ \f -> cm <- updateCurrencies $ currencies c
DBState hs <- updateHashes c
{ kmCurrency = cm -- TODO not sure how I feel about this, probably will change this struct alot
, kmAccount = am -- in the future so whatever...for now
, kmBudgetInterval = intervalMaybeBounds $ budgetInterval $ global c return $ \f ->
, kmStatementInterval = intervalMaybeBounds $ statementInterval $ global c DBState
, kmNewCommits = hs { kmCurrency = cm
, kmConfigDir = f , kmAccount = am
} , kmBudgetInterval = budgetInt
, kmStatementInterval = statementInt
, kmNewCommits = hs
, kmConfigDir = f
}

View File

@ -8,6 +8,7 @@ module Internal.Insert
) )
where where
import Data.Bitraversable
import Data.Hashable import Data.Hashable
import Database.Persist.Class import Database.Persist.Class
import Database.Persist.Sql hiding (Single, Statement) import Database.Persist.Sql hiding (Single, Statement)
@ -48,19 +49,21 @@ lookupCurrency c = do
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- intervals -- intervals
expandDatePat :: Bounds -> DatePat -> [Day] expandDatePat :: Bounds -> DatePat -> EitherErr [Day]
expandDatePat (a, b) (Cron cp) = filter (cronPatternMatches cp) [a .. b] expandDatePat (a, b) (Cron cp) = return $ filter (cronPatternMatches cp) [a .. b]
expandDatePat i (Mod mp) = expandModPat mp i expandDatePat i (Mod mp) = expandModPat mp i
expandModPat :: ModPat -> Bounds -> [Day] expandModPat :: ModPat -> Bounds -> EitherErr [Day]
expandModPat expandModPat
ModPat {mpStart = s, mpBy = b, mpUnit = u, mpRepeats = r} ModPat {mpStart = s, mpBy = b, mpUnit = u, mpRepeats = r}
(lower, upper) = (lower, upper) = do
takeWhile (<= upper) $ start <- maybe (return lower) fromGregorian' s
(`addFun` start) . (* b') return $
<$> maybe id (take . fromIntegral) r [0 ..] takeWhile (<= upper) $
(`addFun` start) . (* b')
<$> maybe id (take . fromIntegral) r [0 ..]
where where
start = maybe lower fromGregorian' s -- start = maybe lower fromGregorian' s
b' = fromIntegral b b' = fromIntegral b
addFun = case u of addFun = case u of
Day -> addDays Day -> addDays
@ -118,8 +121,9 @@ mdyPatternMatches check x p = case p of
insertBudget :: MonadUnliftIO m => Budget -> MappingT m [InsertError] insertBudget :: MonadUnliftIO m => Budget -> MappingT m [InsertError]
insertBudget Budget {income = is, expenses = es} = do insertBudget Budget {income = is, expenses = es} = do
mapM_ insertExpense es es1 <- mapM insertIncome is
concat <$> mapM insertIncome is es2 <- mapM insertExpense es
return $ concat $ es1 ++ es2
-- TODO this hashes twice (not that it really matters) -- TODO this hashes twice (not that it really matters)
whenHash whenHash
@ -144,21 +148,23 @@ insertIncome
} = } =
whenHash CTIncome i [] $ \c -> do whenHash CTIncome i [] $ \c -> do
bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval
unlessLeft (balanceIncome i) $ \balanced -> do case (balanceIncome i, expandDatePat bounds dp) of
forM_ (expandDatePat bounds dp) $ \day -> do (Right balanced, Right days) -> do
alloTx <- concat <$> mapM (allocationToTx from day) balanced forM_ days $ \day -> do
taxTx <- fmap (,Fixed) <$> mapM (taxToTx from day cur) ts alloTx <- concat <$> mapM (allocationToTx from day) balanced
lift $ mapM_ (\(t, b) -> insertTxBucket (Just b) c t) $ alloTx ++ taxTx taxTx <- fmap (,Fixed) <$> mapM (taxToTx from day cur) ts
lift $ mapM_ (\(t, b) -> insertTxBucket (Just b) c t) $ alloTx ++ taxTx
return []
(a, b) -> return $ catMaybes [leftToMaybe a, leftToMaybe b]
balanceIncome :: Income -> EitherErr [BalAllocation] balanceIncome :: Income -> EitherErr [BalAllocation]
balanceIncome balanceIncome
Income Income
{ incGross = g { incGross = g
, incWhen = dp
, incPretax = pre , incPretax = pre
, incTaxes = tax , incTaxes = tax
, incPosttax = post , incPosttax = post
} = (preRat ++) <$> balancePostTax dp bal postRat } = (preRat ++) <$> balancePostTax bal postRat
where where
preRat = mapAlloAmts dec2Rat <$> pre preRat = mapAlloAmts dec2Rat <$> pre
postRat = mapAlloAmts (fmap dec2Rat) <$> post postRat = mapAlloAmts (fmap dec2Rat) <$> post
@ -229,7 +235,7 @@ transferToTx
transferToTx day from to cur Amount {amtValue = v, amtDesc = d} = transferToTx day from to cur Amount {amtValue = v, amtDesc = d} =
txPair day from to cur v d txPair day from to cur v d
insertExpense :: MonadUnliftIO m => Expense -> MappingT m () insertExpense :: MonadUnliftIO m => Expense -> MappingT m [InsertError]
insertExpense insertExpense
e@Expense e@Expense
{ expFrom = from { expFrom = from
@ -238,11 +244,12 @@ insertExpense
, expBucket = buc , expBucket = buc
, expAmounts = as , expAmounts = as
} = do } = do
whenHash CTExpense e () $ \key -> mapM_ (go key) as whenHash CTExpense e [] $ \key -> concat <$> mapM (go key) as
where where
go key amt = do go key amt = do
keys <- timeAmountToTx from to cur amt res <- timeAmountToTx from to cur amt
lift $ mapM_ (insertTxBucket (Just buc) key) keys unlessLeft res $
lift . mapM_ (insertTxBucket (Just buc) key)
timeAmountToTx timeAmountToTx
:: MonadUnliftIO m :: MonadUnliftIO m
@ -250,7 +257,7 @@ timeAmountToTx
-> AcntID -> AcntID
-> CurID -> CurID
-> TimeAmount -> TimeAmount
-> MappingT m [KeyTx] -> MappingT m (EitherErr [KeyTx])
timeAmountToTx timeAmountToTx
from from
to to
@ -264,7 +271,7 @@ timeAmountToTx
} }
} = do } = do
bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval
mapM tx $ expandDatePat bounds dp bimapM return (mapM tx) $ expandDatePat bounds dp
where where
tx day = txPair day from to cur (dec2Rat v) d tx day = txPair day from to cur (dec2Rat v) d
@ -277,10 +284,10 @@ insertStatements conf = concat <$> mapM insertStatement (statements conf)
-- unless (null es) $ throwIO $ InsertException es -- unless (null es) $ throwIO $ InsertException es
insertStatement :: MonadUnliftIO m => Statement -> MappingT m [InsertError] insertStatement :: MonadUnliftIO m => Statement -> MappingT m [InsertError]
insertStatement (StmtManual m) = insertManual m >> return [] insertStatement (StmtManual m) = insertManual m
insertStatement (StmtImport i) = insertImport i insertStatement (StmtImport i) = insertImport i
insertManual :: MonadUnliftIO m => Manual -> MappingT m () insertManual :: MonadUnliftIO m => Manual -> MappingT m [InsertError]
insertManual insertManual
m@Manual m@Manual
{ manualDate = dp { manualDate = dp
@ -290,10 +297,11 @@ insertManual
, manualCurrency = u , manualCurrency = u
, manualDesc = e , manualDesc = e
} = do } = do
whenHash CTManual m () $ \c -> do whenHash CTManual m [] $ \c -> do
bounds <- (liftIO . resolveBounds) =<< asks kmStatementInterval bounds <- (liftIO . resolveBounds) =<< asks kmStatementInterval
ts <- mapM tx $ expandDatePat bounds dp unlessLeft (expandDatePat bounds dp) $ \days -> do
lift $ mapM_ (insertTx c) ts ts <- mapM tx days
lift $ mapM_ (insertTx c) ts
where where
tx day = txPair day from to u (dec2Rat v) e tx day = txPair day from to u (dec2Rat v) e

View File

@ -102,24 +102,27 @@ recoverZipper :: Zipped a -> [a]
recoverZipper (Zipped as bs) = reverse as ++ bs recoverZipper (Zipped as bs) = reverse as ++ bs
zipperSlice zipperSlice
:: (a -> b -> Ordering) :: Monad m
=> (a -> b -> m Ordering)
-> b -> b
-> Zipped a -> Zipped a
-> Either (Zipped a) (Unzipped a) -> m (Either (Zipped a) (Unzipped a))
zipperSlice f x = go zipperSlice f x = go
where where
go z@(Zipped _ []) = Left z go z@(Zipped _ []) = return $ Left z
go z@(Zipped bs (a : as)) = go z@(Zipped bs (a : as)) = do
case f a x of res <- f a x
case res of
GT -> go $ Zipped (a : bs) as GT -> go $ Zipped (a : bs) as
EQ -> Right $ goEq (Unzipped bs [a] as) EQ -> Right <$> goEq (Unzipped bs [a] as)
LT -> Left z LT -> return $ Left z
goEq z@(Unzipped _ _ []) = z goEq z@(Unzipped _ _ []) = return z
goEq z@(Unzipped bs cs (a : as)) = goEq z@(Unzipped bs cs (a : as)) = do
case f a x of res <- f a x
case res of
GT -> goEq $ Unzipped (a : bs) cs as GT -> goEq $ Unzipped (a : bs) cs as
EQ -> goEq $ Unzipped bs (a : cs) as EQ -> goEq $ Unzipped bs (a : cs) as
LT -> z LT -> return z
zipperMatch :: Unzipped Match -> TxRecord -> EitherErrs (Zipped Match, MatchRes RawTx) zipperMatch :: Unzipped Match -> TxRecord -> EitherErrs (Zipped Match, MatchRes RawTx)
zipperMatch (Unzipped bs cs as) x = go [] cs zipperMatch (Unzipped bs cs as) x = go [] cs
@ -175,8 +178,9 @@ matchDates ms = go ([], [], initZipper ms)
, reverse unmatched , reverse unmatched
, recoverZipper z , recoverZipper z
) )
go (matched, unmatched, z) (r : rs) = go (matched, unmatched, z) (r : rs) = do
case zipperSlice findDate r z of sliced <- zipperSlice findDate r z
case sliced of
Left zipped -> go (matched, r : unmatched, zipped) rs Left zipped -> go (matched, r : unmatched, zipped) rs
Right unzipped -> do Right unzipped -> do
(z', res) <- zipperMatch unzipped r (z', res) <- zipperMatch unzipped r
@ -185,7 +189,7 @@ matchDates ms = go ([], [], initZipper ms)
MatchSkip -> (Nothing : matched, unmatched) MatchSkip -> (Nothing : matched, unmatched)
MatchFail -> (matched, r : unmatched) MatchFail -> (matched, r : unmatched)
go (m, u, z') rs go (m, u, z') rs
findDate m r = maybe EQ (`compareDate` trDate r) $ mDate m findDate m r = maybe (Right EQ) (first (: []) . (`compareDate` trDate r)) $ mDate m
matchNonDates :: [Match] -> [TxRecord] -> EitherErrs ([RawTx], [TxRecord], [Match]) matchNonDates :: [Match] -> [TxRecord] -> EitherErrs ([RawTx], [TxRecord], [Match])
matchNonDates ms = go ([], [], initZipper ms) matchNonDates ms = go ([], [], initZipper ms)

View File

@ -540,8 +540,8 @@ data AllocationSuberr
data InsertError data InsertError
= RegexError T.Text = RegexError T.Text
| -- | YearError MatchYMD | YearError Natural
ConversionError T.Text | ConversionError T.Text
| LookupError LookupSuberr T.Text | LookupError LookupSuberr T.Text
| BalanceError BalanceType CurID [RawSplit] | BalanceError BalanceType CurID [RawSplit]
| AllocationError AllocationSuberr DatePat | AllocationError AllocationSuberr DatePat

View File

@ -2,25 +2,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
module Internal.Utils module Internal.Utils where
( compareDate
, intervalMaybeBounds
, fmtRational
, matches
, fromGregorian'
, resolveBounds
, leftToMaybe
, dec2Rat
, concatEithers2
, concatEither2
, parseRational
, showError
, unlessLeft
, unlessLefts
, inMaybeBounds
, acntPath2Text
)
where
import Data.Time.Format.ISO8601 import Data.Time.Format.ISO8601
import GHC.Real import GHC.Real
@ -33,39 +15,49 @@ import RIO.Time
import Text.Regex.TDFA import Text.Regex.TDFA
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- dates -- gregorian
gregTup :: Gregorian -> (Integer, Int, Int) gregTup :: Gregorian -> EitherErr (Integer, Int, Int)
gregTup Gregorian {..} = gregTup Gregorian {..}
( fromIntegral gYear | gYear > 99 = Left $ YearError gYear
, fromIntegral gMonth | otherwise =
, fromIntegral gDay return
) ( fromIntegral gYear + 2000
, fromIntegral gMonth
, fromIntegral gDay
)
gregMTup :: GregorianM -> (Integer, Int) gregMTup :: GregorianM -> EitherErr (Integer, Int)
gregMTup GregorianM {..} = gregMTup GregorianM {..}
( fromIntegral gmYear | gmYear > 99 = Left $ YearError gmYear
, fromIntegral gmMonth | otherwise =
) return
( fromIntegral gmYear + 2000
, fromIntegral gmMonth
)
data YMD_ = Y_ Integer | YM_ Integer Int | YMD_ Integer Int Int data MDY_ = Y_ Integer | YM_ Integer Int | YMD_ Integer Int Int
fromMatchYMD :: MatchYMD -> YMD_ fromMatchYMD :: MatchYMD -> EitherErr MDY_
fromMatchYMD m = case m of fromMatchYMD m = case m of
Y y -> Y_ $ fromIntegral y Y y
YM g -> uncurry YM_ $ gregMTup g | y > 99 -> Left $ YearError y
YMD g -> uncurry3 YMD_ $ gregTup g | otherwise -> Right $ Y_ $ fromIntegral y + 2000
YM g -> uncurry YM_ <$> gregMTup g
YMD g -> uncurry3 YMD_ <$> gregTup g
compareDate :: MatchDate -> Day -> Ordering compareDate :: MatchDate -> Day -> EitherErr Ordering
compareDate (On md) x = compareDate (On md) x = do
case fromMatchYMD md of res <- fromMatchYMD md
return $ case res of
Y_ y' -> compare y y' Y_ y' -> compare y y'
YM_ y' m' -> compare (y, m) (y', m') YM_ y' m' -> compare (y, m) (y', m')
YMD_ y' m' d' -> compare (y, m, d) (y', m', d') YMD_ y' m' d' -> compare (y, m, d) (y', m', d')
where where
(y, m, d) = toGregorian x (y, m, d) = toGregorian x
compareDate (In md offset) x = do compareDate (In md offset) x = do
case fromMatchYMD md of res <- fromMatchYMD md
return $ case res of
Y_ y' -> compareRange y' y Y_ y' -> compareRange y' y
YM_ y' m' -> compareRange (toMonth y' m') $ toMonth y m YM_ y' m' -> compareRange (toMonth y' m') $ toMonth y m
YMD_ y' m' d' -> YMD_ y' m' d' ->
@ -78,21 +70,26 @@ compareDate (In md offset) x = do
| otherwise = if (start + fromIntegral offset - 1) < z then GT else EQ | otherwise = if (start + fromIntegral offset - 1) < z then GT else EQ
toMonth year month = (year * 12) + fromIntegral month toMonth year month = (year * 12) + fromIntegral month
-- boundsFromGregorian :: (Gregorian, Gregorian) -> Bounds boundsFromGregorian :: (Gregorian, Gregorian) -> EitherErrs Bounds
-- boundsFromGregorian (a, b) = (fromGregorian' a, fromGregorian' b) boundsFromGregorian (a, b) = concatEither2 a_ b_ (,)
where
a_ = fromGregorian' a
b_ = fromGregorian' b
fromGregorian' :: Gregorian -> Day fromGregorian' :: Gregorian -> EitherErr Day
fromGregorian' = uncurry3 fromGregorian . gregTup fromGregorian' = fmap (uncurry3 fromGregorian) . gregTup
-- inBounds :: Bounds -> Day -> Bool inBounds :: Bounds -> Day -> Bool
-- inBounds (d0, d1) x = d0 <= x && x <= d1 inBounds (d0, d1) x = d0 <= x && x <= d1
inMaybeBounds :: MaybeBounds -> Day -> Bool inMaybeBounds :: MaybeBounds -> Day -> Bool
inMaybeBounds (d0, d1) x = maybe True (x >=) d0 && maybe True (x <=) d1 inMaybeBounds (d0, d1) x = maybe True (x >=) d0 && maybe True (x <=) d1
intervalMaybeBounds :: Interval -> MaybeBounds intervalMaybeBounds :: Interval -> EitherErrs MaybeBounds
intervalMaybeBounds Interval {intStart = s, intEnd = e} = intervalMaybeBounds Interval {intStart = s, intEnd = e} = concatEither2 s_ e_ (,)
(fmap fromGregorian' s, fmap fromGregorian' e) where
s_ = mapM fromGregorian' s
e_ = mapM fromGregorian' e
resolveBounds :: MonadUnliftIO m => MaybeBounds -> m Bounds resolveBounds :: MonadUnliftIO m => MaybeBounds -> m Bounds
resolveBounds (s, e) = do resolveBounds (s, e) = do
@ -107,13 +104,13 @@ resolveBounds (s, e) = do
matches :: Match -> TxRecord -> EitherErrs (MatchRes RawTx) matches :: Match -> TxRecord -> EitherErrs (MatchRes RawTx)
matches Match {..} r@TxRecord {..} = do matches Match {..} r@TxRecord {..} = do
res <- concatEither2 other desc (&&) res <- concatEither3 date other desc (\x y z -> x && y && z)
if date && val && res if val && res
then maybe (Right MatchSkip) (fmap MatchPass . convert) mTx then maybe (Right MatchSkip) (fmap MatchPass . convert) mTx
else Right MatchFail else Right MatchFail
where where
val = valMatches mVal trAmount val = valMatches mVal trAmount
date = maybe True (`dateMatches` trDate) mDate date = maybe (Right True) (`dateMatches` trDate) mDate
other = foldM (\a o -> (a &&) <$> otherMatches trOther o) True mOther other = foldM (\a o -> (a &&) <$> otherMatches trOther o) True mOther
desc = maybe (return True) (matchMaybe trDesc) mDesc desc = maybe (return True) (matchMaybe trDesc) mDesc
convert (ToTx cur a ss) = toTx cur a ss r convert (ToTx cur a ss) = toTx cur a ss r
@ -149,8 +146,8 @@ valMatches MatchVal {..} x =
s = signum x >= 0 s = signum x >= 0
checkMaybe = maybe True checkMaybe = maybe True
dateMatches :: MatchDate -> Day -> Bool dateMatches :: MatchDate -> Day -> EitherErr Bool
dateMatches md = (EQ ==) . compareDate md dateMatches md = fmap (EQ ==) . compareDate md
otherMatches :: M.Map T.Text T.Text -> MatchOther -> EitherErr Bool otherMatches :: M.Map T.Text T.Text -> MatchOther -> EitherErr Bool
otherMatches dict m = case m of otherMatches dict m = case m of
@ -246,8 +243,8 @@ readRational s = case T.split (== '.') s of
err = Left $ ConversionError s err = Left $ ConversionError s
-- TODO smells like a lens -- TODO smells like a lens
-- mapTxSplits :: (a -> b) -> Tx a -> Tx b mapTxSplits :: (a -> b) -> Tx a -> Tx b
-- mapTxSplits f t@Tx {txSplits = ss} = t {txSplits = fmap f ss} mapTxSplits f t@Tx {txSplits = ss} = t {txSplits = fmap f ss}
fmtRational :: Natural -> Rational -> T.Text fmtRational :: Natural -> Rational -> T.Text
fmtRational precision x = T.concat [s, txt n', ".", pad 2 "0" $ txt d'] fmtRational precision x = T.concat [s, txt n', ".", pad 2 "0" $ txt d']
@ -275,6 +272,8 @@ acntPath2Text (AcntPath t cs) = T.intercalate "/" (atName t : cs)
showError :: InsertError -> [T.Text] showError :: InsertError -> [T.Text]
showError (StatementError ts ms) = (showTx <$> ts) ++ (showMatch <$> ms) showError (StatementError ts ms) = (showTx <$> ts) ++ (showMatch <$> ms)
showError other = (: []) $ case other of showError other = (: []) $ case other of
-- TODO show whole date here since this is kinda useless
(YearError y) -> T.append "Year must be two digits: " $ showT y
(RegexError re) -> T.append "could not make regex from pattern: " re (RegexError re) -> T.append "could not make regex from pattern: " re
(ConversionError x) -> T.append "Could not convert to rational number: " x (ConversionError x) -> T.append "Could not convert to rational number: " x
-- TODO use the field indicator -- TODO use the field indicator
@ -347,50 +346,24 @@ showMatch Match {mDate = d, mVal = v, mDesc = e, mOther = o, mTimes = n, mPriori
[] -> Nothing [] -> Nothing
xs -> Just $ T.concat $ showMatchOther <$> xs xs -> Just $ T.concat $ showMatchOther <$> xs
-- | Convert match date to text
-- Single date matches will just show the single date, and ranged matches will
-- show an interval like [YY-MM-DD, YY-MM-DD)
showMatchDate :: MatchDate -> T.Text showMatchDate :: MatchDate -> T.Text
showMatchDate md = case md of showMatchDate md = case md of
(On x) -> showMatchYMD x (On x) ->
(In start n) -> T.concat ["[", showMatchYMD start, " ", showYMD_ end, ")"] let ys = case x of
where Y y -> [y]
-- TODO not DRY (this shifting thing happens during the comparison YM (GregorianM {..}) -> [gmYear, gmMonth]
-- function (kinda) YMD (Gregorian {..}) -> [gYear, gMonth, gDay]
end = case fromMatchYMD start of in T.intercalate "-" $ L.take 3 (fmap showT ys ++ L.repeat "*")
Y_ y -> Y_ $ y + fromIntegral n (In _ _) -> undefined
YM_ y m ->
let (y_, m_) = divMod (m + fromIntegral n - 1) 12
in YM_ (y + fromIntegral y_) (m + m_ + 1)
YMD_ y m d ->
uncurry3 YMD_ $
toGregorian $
addDays (fromIntegral n) $
fromGregorian y m d
-- | convert YMD match to text -- let ys = case x of
showMatchYMD :: MatchYMD -> T.Text -- Y y -> [y]
showMatchYMD = showYMD_ . fromMatchYMD -- YM (GregorianM {..}) -> [gmYear, gmMonth]
-- YMD (Gregorian {..}) -> [gYear, gMonth, gDay]
-- in T.intercalate "-" $ L.take 3 (fmap showT ys ++ L.repeat "*")
showYMD_ :: YMD_ -> T.Text
showYMD_ md =
T.intercalate "-" $ L.take 3 (fmap showT digits ++ L.repeat "*")
where
digits = case md of
Y_ y -> [fromIntegral y]
YM_ y m -> [fromIntegral y, m]
YMD_ y m d -> [fromIntegral y, m, d]
-- TODO there are errors that can be thrown here
showMatchVal :: MatchVal -> Maybe T.Text showMatchVal :: MatchVal -> Maybe T.Text
showMatchVal MatchVal {mvSign = Nothing, mvNum = Nothing, mvDen = Nothing} = Nothing showMatchVal = undefined
showMatchVal MatchVal {..} = Just $ T.concat [sign, num, ".", den]
where
sign = case mvSign of
Nothing -> "+/-"
Just s -> if s then "+" else "-"
num = maybe "*" showT mvNum
den = maybe "*" (lpadT '0' (fromIntegral mvPrec) . showT) mvDen
showMatchOther :: MatchOther -> T.Text showMatchOther :: MatchOther -> T.Text
showMatchOther = undefined showMatchOther = undefined
@ -427,21 +400,21 @@ concatEither2 a b fun = case (a, b) of
(Right a_, Right b_) -> Right $ fun a_ b_ (Right a_, Right b_) -> Right $ fun a_ b_
_ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b] _ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b]
-- concatEither3 :: Either x a -> Either x b -> Either x c -> (a -> b -> c -> d) -> Either [x] d concatEither3 :: Either x a -> Either x b -> Either x c -> (a -> b -> c -> d) -> Either [x] d
-- concatEither3 a b c fun = case (a, b, c) of concatEither3 a b c fun = case (a, b, c) of
-- (Right a_, Right b_, Right c_) -> Right $ fun a_ b_ c_ (Right a_, Right b_, Right c_) -> Right $ fun a_ b_ c_
-- _ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b, leftToMaybe c] _ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b, leftToMaybe c]
concatEithers2 :: Either [x] a -> Either [x] b -> (a -> b -> c) -> Either [x] c concatEithers2 :: Either [x] a -> Either [x] b -> (a -> b -> c) -> Either [x] c
concatEithers2 a b = merge . concatEither2 a b concatEithers2 a b = merge . concatEither2 a b
-- concatEithers3 concatEithers3
-- :: Either [x] a :: Either [x] a
-- -> Either [x] b -> Either [x] b
-- -> Either [x] c -> Either [x] c
-- -> (a -> b -> c -> d) -> (a -> b -> c -> d)
-- -> Either [x] d -> Either [x] d
-- concatEithers3 a b c = merge . concatEither3 a b c concatEithers3 a b c = merge . concatEither3 a b c
concatEitherL :: [Either x a] -> Either [x] [a] concatEitherL :: [Either x a] -> Either [x] [a]
concatEitherL as = case partitionEithers as of concatEitherL as = case partitionEithers as of
@ -473,20 +446,17 @@ merge = first concat
-- random functions -- random functions
-- when bifunctor fails... -- when bifunctor fails...
-- thrice :: (a -> d) -> (b -> e) -> (c -> f) -> (a, b, c) -> (d, e, f) thrice :: (a -> d) -> (b -> e) -> (c -> f) -> (a, b, c) -> (d, e, f)
-- thrice f1 f2 f3 (x, y, z) = (f1 x, f2 y, f3 z) thrice f1 f2 f3 (x, y, z) = (f1 x, f2 y, f3 z)
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 f (a, b, c) = f a b c uncurry3 f (a, b, c) = f a b c
-- lpad :: a -> Int -> [a] -> [a] lpad :: a -> Int -> [a] -> [a]
-- lpad c n s = replicate (n - length s) c ++ s lpad c n s = replicate (n - length s) c ++ s
-- rpad :: a -> Int -> [a] -> [a] rpad :: a -> Int -> [a] -> [a]
-- rpad c n s = s ++ replicate (n - length s) c rpad c n s = s ++ replicate (n - length s) c
lpadT :: Char -> Int -> T.Text -> T.Text
lpadT c n s = T.append (T.replicate (n - T.length s) (T.singleton c)) s
matchMaybe :: RegexContext Regex query b => query -> T.Text -> EitherErr b matchMaybe :: RegexContext Regex query b => query -> T.Text -> EitherErr b
matchMaybe q re = first (const $ RegexError re) $ pureTry $ q =~ re matchMaybe q re = first (const $ RegexError re) $ pureTry $ q =~ re