module Penny.Brenner.Types
( Date(..)
, IncDec(..)
, UNumber(..)
, FitId(..)
, Payee(..)
, Desc(..)
, Amount(unAmount)
, mkAmount
, translate
, DbMap
, DbList
, Posting(..)
, ConfigLocation(..)
, DbLocation(..)
, FitAcctName(..)
, FitAcctDesc(..)
, ParserDesc(..)
, PennyAcct(..)
, Translator(..)
, DefaultAcct(..)
, Currency(..)
, FitAcct(..)
, Config(..)
, FitFileLocation(..)
, AllowNew(..)
, ParserFn
) where
import Control.Applicative ((<$>), (<*>))
import qualified Control.Monad.Exception.Synchronous as Ex
import qualified Data.Map as M
import qualified Data.Time as Time
import qualified Penny.Copper.Render as R
import qualified Penny.Lincoln as L
import Data.Text (Text, pack, unpack)
import qualified Data.Text.Encoding as E
import qualified Data.Serialize as S
newtype Date = Date { unDate :: Time.Day }
deriving (Eq, Show, Ord, Read)
instance S.Serialize Date where
put = S.put . show . unDate
get = Date <$> (read <$> S.get)
data IncDec
= Increase
| Decrease
deriving (Eq, Show, Read)
instance S.Serialize IncDec where
put x = case x of
Increase -> S.putWord8 0
Decrease -> S.putWord8 1
get = S.getWord8 >>= f
where
f x = case x of
0 -> return Increase
1 -> return Decrease
_ -> fail "read IncDec error"
newtype UNumber = UNumber { unUNumber :: Integer }
deriving (Eq, Show, Ord, Read)
instance S.Serialize UNumber where
put = S.put . unUNumber
get = UNumber <$> S.get
putText :: Text -> S.Put
putText = S.put . E.encodeUtf8
getText :: S.Get Text
getText = S.get >>= f
where
f bs = case E.decodeUtf8' bs of
Left _ -> fail "text reading failed"
Right x -> return x
newtype FitId = FitId { unFitId :: Text }
deriving (Eq, Show, Ord, Read)
instance S.Serialize FitId where
put = putText . unFitId
get = FitId <$> getText
newtype Payee = Payee { unPayee :: Text }
deriving (Eq, Show, Ord, Read)
instance S.Serialize Payee where
put = putText . unPayee
get = Payee <$> getText
newtype Desc =
Desc { unDesc :: Text }
deriving (Eq, Show, Ord, Read)
instance S.Serialize Desc where
put = putText . unDesc
get = Desc <$> getText
newtype Amount = Amount { unAmount :: Text }
deriving (Eq, Show, Ord, Read)
instance S.Serialize Amount where
put = putText . unAmount
get = getText >>= f
where
f x = case mkAmount . unpack $ x of
Nothing -> fail $ "failed to load amount: " ++ unpack x
Just a -> return a
mkAmount :: String -> Maybe Amount
mkAmount s =
let isDigit c = c >= '0' && c <= '9'
(_, rs) = span isDigit s
in case rs of
"" -> if not . null $ s
then return . Amount . pack $ s
else Nothing
'.':rest -> if all isDigit rest
then return . Amount . pack $ s
else Nothing
_ -> Nothing
translate
:: IncDec
-> Translator
-> L.DrCr
translate Increase IncreaseIsDebit = L.Debit
translate Increase IncreaseIsCredit = L.Credit
translate Decrease IncreaseIsDebit = L.Credit
translate Decrease IncreaseIsCredit = L.Debit
type DbMap = M.Map UNumber Posting
type DbList = [(UNumber, Posting)]
data Posting = Posting
{ date :: Date
, desc :: Desc
, incDec :: IncDec
, amount :: Amount
, payee :: Payee
, fitId :: FitId
} deriving (Read, Show)
instance S.Serialize Posting where
put x = S.put (date x)
>> S.put (desc x)
>> S.put (incDec x)
>> S.put (amount x)
>> S.put (payee x)
>> S.put (fitId x)
get = Posting
<$> S.get
<*> S.get
<*> S.get
<*> S.get
<*> S.get
<*> S.get
newtype ConfigLocation = ConfigLocation
{ unConfigLocation :: Text }
deriving (Eq, Show)
instance L.HasText ConfigLocation where text = unConfigLocation
newtype DbLocation = DbLocation { unDbLocation :: Text }
deriving (Eq, Show)
instance L.HasText DbLocation where text = unDbLocation
newtype FitAcctDesc = FitAcctDesc { unFitAcctDesc :: Text }
deriving (Eq, Show)
instance L.HasText FitAcctDesc where text = unFitAcctDesc
newtype ParserDesc = ParserDesc { unParserDesc :: Text }
deriving (Eq, Show)
instance L.HasText ParserDesc where text = unParserDesc
newtype FitAcctName = FitAcctName { unFitAcctName :: Text }
deriving (Eq, Show)
instance L.HasText FitAcctName where text = unFitAcctName
newtype PennyAcct = PennyAcct { unPennyAcct :: L.Account }
deriving (Eq, Show)
instance L.HasTextList PennyAcct where
textList = L.textList . unPennyAcct
data Translator
= IncreaseIsDebit
| IncreaseIsCredit
deriving (Eq, Show)
newtype DefaultAcct = DefaultAcct { unDefaultAcct :: L.Account }
deriving (Eq, Show)
instance L.HasTextList DefaultAcct where
textList = L.textList . unDefaultAcct
newtype Currency = Currency { unCurrency :: L.Commodity }
deriving (Eq, Show)
instance L.HasText Currency where text = L.text . unCurrency
data FitAcct = FitAcct
{ fitAcctName :: FitAcctName
, fitAcctDesc :: FitAcctDesc
, dbLocation :: DbLocation
, pennyAcct :: PennyAcct
, defaultAcct :: DefaultAcct
, currency :: Currency
, groupSpecs :: R.GroupSpecs
, translator :: Translator
, side :: L.Side
, spaceBetween :: L.SpaceBetween
, parser :: ( ParserDesc
, FitFileLocation -> IO (Ex.Exceptional String [Posting]))
, toLincolnPayee :: Desc -> Payee -> L.Payee
}
data Config = Config
{ defaultFitAcct :: Maybe FitAcct
, moreFitAccts :: [FitAcct]
}
newtype FitFileLocation = FitFileLocation { unFitFileLocation :: String }
deriving (Show, Eq)
newtype AllowNew = AllowNew { unAllowNew :: Bool }
deriving (Show, Eq)
type ParserFn
= FitFileLocation
-> IO (Ex.Exceptional String [Posting])