ref: move Posting account name fns to AccountName
accountNamePostingType accountNameWithoutPostingType accountNameWithPostingType joinAccountNames concatAccountNames accountNameApplyAliases accountNameApplyAliasesMemo
This commit is contained in:
parent
5a0d61998a
commit
44211c1c57
@ -45,18 +45,28 @@ module Hledger.Data.AccountName (
|
|||||||
,subAccountNamesFrom
|
,subAccountNamesFrom
|
||||||
,topAccountNames
|
,topAccountNames
|
||||||
,unbudgetedAccountName
|
,unbudgetedAccountName
|
||||||
|
,accountNamePostingType
|
||||||
|
,accountNameWithoutPostingType
|
||||||
|
,accountNameWithPostingType
|
||||||
|
,joinAccountNames
|
||||||
|
,concatAccountNames
|
||||||
|
,accountNameApplyAliases
|
||||||
|
,accountNameApplyAliasesMemo
|
||||||
,tests_AccountName
|
,tests_AccountName
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
|
import Control.Monad (foldM)
|
||||||
import Data.Foldable (asum, toList)
|
import Data.Foldable (asum, toList)
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
import Data.MemoUgly (memo)
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Tree (Tree(..))
|
import Data.Tree (Tree(..))
|
||||||
|
import Safe
|
||||||
import Text.DocLayout (realLength)
|
import Text.DocLayout (realLength)
|
||||||
|
|
||||||
import Hledger.Data.Types
|
import Hledger.Data.Types
|
||||||
@ -132,6 +142,65 @@ accountNameLevel a = T.length (T.filter (==acctsepchar) a) + 1
|
|||||||
unbudgetedAccountName :: T.Text
|
unbudgetedAccountName :: T.Text
|
||||||
unbudgetedAccountName = "<unbudgeted>"
|
unbudgetedAccountName = "<unbudgeted>"
|
||||||
|
|
||||||
|
accountNamePostingType :: AccountName -> PostingType
|
||||||
|
accountNamePostingType a
|
||||||
|
| T.null a = RegularPosting
|
||||||
|
| T.head a == '[' && T.last a == ']' = BalancedVirtualPosting
|
||||||
|
| T.head a == '(' && T.last a == ')' = VirtualPosting
|
||||||
|
| otherwise = RegularPosting
|
||||||
|
|
||||||
|
accountNameWithoutPostingType :: AccountName -> AccountName
|
||||||
|
accountNameWithoutPostingType a = case accountNamePostingType a of
|
||||||
|
BalancedVirtualPosting -> textUnbracket a
|
||||||
|
VirtualPosting -> textUnbracket a
|
||||||
|
RegularPosting -> a
|
||||||
|
|
||||||
|
accountNameWithPostingType :: PostingType -> AccountName -> AccountName
|
||||||
|
accountNameWithPostingType BalancedVirtualPosting = wrap "[" "]" . accountNameWithoutPostingType
|
||||||
|
accountNameWithPostingType VirtualPosting = wrap "(" ")" . accountNameWithoutPostingType
|
||||||
|
accountNameWithPostingType RegularPosting = accountNameWithoutPostingType
|
||||||
|
|
||||||
|
-- | Prefix one account name to another, preserving posting type
|
||||||
|
-- indicators like concatAccountNames.
|
||||||
|
joinAccountNames :: AccountName -> AccountName -> AccountName
|
||||||
|
joinAccountNames a b = concatAccountNames $ filter (not . T.null) [a,b]
|
||||||
|
|
||||||
|
-- | Join account names into one. If any of them has () or [] posting type
|
||||||
|
-- indicators, these (the first type encountered) will also be applied to
|
||||||
|
-- the resulting account name.
|
||||||
|
concatAccountNames :: [AccountName] -> AccountName
|
||||||
|
concatAccountNames as = accountNameWithPostingType t $ T.intercalate ":" $ map accountNameWithoutPostingType as
|
||||||
|
where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as
|
||||||
|
|
||||||
|
-- | Rewrite an account name using all matching aliases from the given list, in sequence.
|
||||||
|
-- Each alias sees the result of applying the previous aliases.
|
||||||
|
-- Or, return any error arising from a bad regular expression in the aliases.
|
||||||
|
accountNameApplyAliases :: [AccountAlias] -> AccountName -> Either RegexError AccountName
|
||||||
|
accountNameApplyAliases aliases a =
|
||||||
|
let (aname,atype) = (accountNameWithoutPostingType a, accountNamePostingType a)
|
||||||
|
in foldM
|
||||||
|
(\acct alias -> dbg6 "result" $ aliasReplace (dbg6 "alias" alias) (dbg6 "account" acct))
|
||||||
|
aname
|
||||||
|
aliases
|
||||||
|
>>= Right . accountNameWithPostingType atype
|
||||||
|
|
||||||
|
-- | Memoising version of accountNameApplyAliases, maybe overkill.
|
||||||
|
accountNameApplyAliasesMemo :: [AccountAlias] -> AccountName -> Either RegexError AccountName
|
||||||
|
accountNameApplyAliasesMemo aliases = memo (accountNameApplyAliases aliases)
|
||||||
|
-- XXX re-test this memoisation
|
||||||
|
|
||||||
|
-- aliasMatches :: AccountAlias -> AccountName -> Bool
|
||||||
|
-- aliasMatches (BasicAlias old _) a = old `isAccountNamePrefixOf` a
|
||||||
|
-- aliasMatches (RegexAlias re _) a = regexMatchesCI re a
|
||||||
|
|
||||||
|
aliasReplace :: AccountAlias -> AccountName -> Either RegexError AccountName
|
||||||
|
aliasReplace (BasicAlias old new) a
|
||||||
|
| old `isAccountNamePrefixOf` a || old == a =
|
||||||
|
Right $ new <> T.drop (T.length old) a
|
||||||
|
| otherwise = Right a
|
||||||
|
aliasReplace (RegexAlias re repl) a =
|
||||||
|
fmap T.pack . regexReplace re repl $ T.unpack a -- XXX
|
||||||
|
|
||||||
-- | Remove some number of account name components from the front of the account name.
|
-- | Remove some number of account name components from the front of the account name.
|
||||||
-- If the special "<unbudgeted>" top-level account is present, it is preserved and
|
-- If the special "<unbudgeted>" top-level account is present, it is preserved and
|
||||||
-- dropping affects the rest of the account name.
|
-- dropping affects the rest of the account name.
|
||||||
|
|||||||
@ -48,13 +48,6 @@ module Hledger.Data.Posting (
|
|||||||
isPostingInDateSpan',
|
isPostingInDateSpan',
|
||||||
-- * account name operations
|
-- * account name operations
|
||||||
accountNamesFromPostings,
|
accountNamesFromPostings,
|
||||||
accountNamePostingType,
|
|
||||||
accountNameWithoutPostingType,
|
|
||||||
accountNameWithPostingType,
|
|
||||||
joinAccountNames,
|
|
||||||
concatAccountNames,
|
|
||||||
accountNameApplyAliases,
|
|
||||||
accountNameApplyAliasesMemo,
|
|
||||||
-- * comment/tag operations
|
-- * comment/tag operations
|
||||||
commentJoin,
|
commentJoin,
|
||||||
commentAddTag,
|
commentAddTag,
|
||||||
@ -77,12 +70,10 @@ module Hledger.Data.Posting (
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad (foldM)
|
|
||||||
import Data.Default (def)
|
import Data.Default (def)
|
||||||
import Data.Foldable (asum)
|
import Data.Foldable (asum)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import Data.Maybe (fromMaybe, isJust)
|
import Data.Maybe (fromMaybe, isJust)
|
||||||
import Data.MemoUgly (memo)
|
|
||||||
import Data.List (foldl', sort, union)
|
import Data.List (foldl', sort, union)
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
@ -90,7 +81,7 @@ import qualified Data.Text as T
|
|||||||
import qualified Data.Text.Lazy as TL
|
import qualified Data.Text.Lazy as TL
|
||||||
import qualified Data.Text.Lazy.Builder as TB
|
import qualified Data.Text.Lazy.Builder as TB
|
||||||
import Data.Time.Calendar (Day)
|
import Data.Time.Calendar (Day)
|
||||||
import Safe (headDef, maximumBound)
|
import Safe (maximumBound)
|
||||||
import Text.DocLayout (realLength)
|
import Text.DocLayout (realLength)
|
||||||
|
|
||||||
import Text.Tabular.AsciiWide
|
import Text.Tabular.AsciiWide
|
||||||
@ -395,38 +386,6 @@ isPostingInDateSpan' SecondaryDate s = spanContainsDate s . postingDate2
|
|||||||
isEmptyPosting :: Posting -> Bool
|
isEmptyPosting :: Posting -> Bool
|
||||||
isEmptyPosting = mixedAmountLooksZero . pamount
|
isEmptyPosting = mixedAmountLooksZero . pamount
|
||||||
|
|
||||||
-- AccountName stuff that depends on PostingType
|
|
||||||
|
|
||||||
accountNamePostingType :: AccountName -> PostingType
|
|
||||||
accountNamePostingType a
|
|
||||||
| T.null a = RegularPosting
|
|
||||||
| T.head a == '[' && T.last a == ']' = BalancedVirtualPosting
|
|
||||||
| T.head a == '(' && T.last a == ')' = VirtualPosting
|
|
||||||
| otherwise = RegularPosting
|
|
||||||
|
|
||||||
accountNameWithoutPostingType :: AccountName -> AccountName
|
|
||||||
accountNameWithoutPostingType a = case accountNamePostingType a of
|
|
||||||
BalancedVirtualPosting -> textUnbracket a
|
|
||||||
VirtualPosting -> textUnbracket a
|
|
||||||
RegularPosting -> a
|
|
||||||
|
|
||||||
accountNameWithPostingType :: PostingType -> AccountName -> AccountName
|
|
||||||
accountNameWithPostingType BalancedVirtualPosting = wrap "[" "]" . accountNameWithoutPostingType
|
|
||||||
accountNameWithPostingType VirtualPosting = wrap "(" ")" . accountNameWithoutPostingType
|
|
||||||
accountNameWithPostingType RegularPosting = accountNameWithoutPostingType
|
|
||||||
|
|
||||||
-- | Prefix one account name to another, preserving posting type
|
|
||||||
-- indicators like concatAccountNames.
|
|
||||||
joinAccountNames :: AccountName -> AccountName -> AccountName
|
|
||||||
joinAccountNames a b = concatAccountNames $ filter (not . T.null) [a,b]
|
|
||||||
|
|
||||||
-- | Join account names into one. If any of them has () or [] posting type
|
|
||||||
-- indicators, these (the first type encountered) will also be applied to
|
|
||||||
-- the resulting account name.
|
|
||||||
concatAccountNames :: [AccountName] -> AccountName
|
|
||||||
concatAccountNames as = accountNameWithPostingType t $ T.intercalate ":" $ map accountNameWithoutPostingType as
|
|
||||||
where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as
|
|
||||||
|
|
||||||
-- | Apply some account aliases to the posting's account name, as described by accountNameApplyAliases.
|
-- | Apply some account aliases to the posting's account name, as described by accountNameApplyAliases.
|
||||||
-- This can fail due to a bad replacement pattern in a regular expression alias.
|
-- This can fail due to a bad replacement pattern in a regular expression alias.
|
||||||
postingApplyAliases :: [AccountAlias] -> Posting -> Either RegexError Posting
|
postingApplyAliases :: [AccountAlias] -> Posting -> Either RegexError Posting
|
||||||
@ -450,35 +409,6 @@ postingApplyCommodityStyles styles p = p{pamount=styleMixedAmount styles $ pamou
|
|||||||
postingAddTags :: Posting -> [Tag] -> Posting
|
postingAddTags :: Posting -> [Tag] -> Posting
|
||||||
postingAddTags p@Posting{ptags} tags = p{ptags=ptags `union` tags}
|
postingAddTags p@Posting{ptags} tags = p{ptags=ptags `union` tags}
|
||||||
|
|
||||||
-- | Rewrite an account name using all matching aliases from the given list, in sequence.
|
|
||||||
-- Each alias sees the result of applying the previous aliases.
|
|
||||||
-- Or, return any error arising from a bad regular expression in the aliases.
|
|
||||||
accountNameApplyAliases :: [AccountAlias] -> AccountName -> Either RegexError AccountName
|
|
||||||
accountNameApplyAliases aliases a =
|
|
||||||
let (aname,atype) = (accountNameWithoutPostingType a, accountNamePostingType a)
|
|
||||||
in foldM
|
|
||||||
(\acct alias -> dbg6 "result" $ aliasReplace (dbg6 "alias" alias) (dbg6 "account" acct))
|
|
||||||
aname
|
|
||||||
aliases
|
|
||||||
>>= Right . accountNameWithPostingType atype
|
|
||||||
|
|
||||||
-- | Memoising version of accountNameApplyAliases, maybe overkill.
|
|
||||||
accountNameApplyAliasesMemo :: [AccountAlias] -> AccountName -> Either RegexError AccountName
|
|
||||||
accountNameApplyAliasesMemo aliases = memo (accountNameApplyAliases aliases)
|
|
||||||
-- XXX re-test this memoisation
|
|
||||||
|
|
||||||
-- aliasMatches :: AccountAlias -> AccountName -> Bool
|
|
||||||
-- aliasMatches (BasicAlias old _) a = old `isAccountNamePrefixOf` a
|
|
||||||
-- aliasMatches (RegexAlias re _) a = regexMatchesCI re a
|
|
||||||
|
|
||||||
aliasReplace :: AccountAlias -> AccountName -> Either RegexError AccountName
|
|
||||||
aliasReplace (BasicAlias old new) a
|
|
||||||
| old `isAccountNamePrefixOf` a || old == a =
|
|
||||||
Right $ new <> T.drop (T.length old) a
|
|
||||||
| otherwise = Right a
|
|
||||||
aliasReplace (RegexAlias re repl) a =
|
|
||||||
fmap T.pack . regexReplace re repl $ T.unpack a -- XXX
|
|
||||||
|
|
||||||
-- | Apply a specified valuation to this posting's amount, using the
|
-- | Apply a specified valuation to this posting's amount, using the
|
||||||
-- provided price oracle, commodity styles, and reference dates.
|
-- provided price oracle, commodity styles, and reference dates.
|
||||||
-- See amountApplyValuation.
|
-- See amountApplyValuation.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user