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