{-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Ledger.Commodity ( Commodity , CommodityInfo(..), HasCommodityInfo(..) , defaultCommodityInfo, defaultPrimaryCommodityInfo , CommodityMap(..), HasCommodityMap(..) , extendByDigits ) where import Control.Lens import Data.IntMap (IntMap, Key) import qualified Data.IntMap as IntMap import Data.Map (Map) import Data.Ratio import Data.Semigroup import Data.Text (Text) import Data.Thyme.Time import Prelude hiding (lookup) -- | Commodities are simply indices into a commodity info map, which relates -- such commodities to the information known about them. type Commodity = Key extendByDigits :: Int extendByDigits = 6 -- | All of the information known about a commodity. data CommodityInfo = CommodityInfo { _commSymbol :: !Text , _commPrecision :: !Int , _commSuffixed :: !Bool , _commSeparated :: !Bool , _commThousands :: !Bool , _commDecimalComma :: !Bool , _commNoMarket :: !Bool , _commBuiltin :: !Bool , _commKnown :: !Bool , _commPrimary :: !Bool , _commHistory :: !(IntMap (Map UTCTime Rational)) } deriving (Eq, Read, Show) makeClassy ''CommodityInfo instance Semigroup CommodityInfo where x <> y = x & commSymbol .~ y^.commSymbol & commPrecision .~ max (x^.commPrecision) (y^.commPrecision) & commSuffixed .~ (x^.commSuffixed || y^.commSuffixed) & commSeparated .~ (x^.commSeparated || y^.commSeparated) & commThousands .~ (x^.commThousands || y^.commThousands) & commDecimalComma .~ (x^.commDecimalComma || y^.commDecimalComma) & commNoMarket .~ (x^.commNoMarket || y^.commNoMarket) & commBuiltin .~ (x^.commBuiltin || y^.commBuiltin) & commKnown .~ (x^.commKnown || y^.commKnown) & commPrimary .~ (x^.commPrimary || y^.commPrimary) & commHistory .~ (x^.commHistory <> y^.commHistory) instance Monoid CommodityInfo where mempty = defaultCommodityInfo x `mappend` y = x <> y -- | Return a 'CommodityInfo' with defaults selected for all fields. It is -- intended that at least one field of the result will be modified -- immediately. defaultCommodityInfo :: CommodityInfo defaultCommodityInfo = CommodityInfo { _commSymbol = "" , _commPrecision = 0 , _commSuffixed = False , _commSeparated = True , _commThousands = True , _commDecimalComma = False , _commNoMarket = False , _commBuiltin = False , _commKnown = False , _commPrimary = False , _commHistory = IntMap.empty } defaultPrimaryCommodityInfo :: Text -> CommodityInfo defaultPrimaryCommodityInfo sym = defaultCommodityInfo & commSymbol .~ sym & commPrecision .~ 2 & commNoMarket .~ True & commKnown .~ True & commPrimary .~ True -- | A commodities map, relating commodity indices to information about -- those commodities. data CommodityMap = CommodityMap { _commodities :: !(IntMap CommodityInfo) } deriving (Eq, Read, Show) makeClassy ''CommodityMap instance Semigroup CommodityMap where CommodityMap x <> CommodityMap y = CommodityMap (IntMap.unionWith (<>) x y) instance Monoid CommodityMap where mempty = CommodityMap mempty x `mappend` y = x <> y