{-|

A general query system for matching things (accounts, postings,
transactions..)  by various criteria, and a SimpleTextParser for query expressions.

-}

{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, ViewPatterns #-}
{-# LANGUAGE CPP #-}

module Hledger.Query (
  -- * Query and QueryOpt
  Query(..),
  QueryOpt(..),
  -- * parsing
  parseQuery,
  simplifyQuery,
  filterQuery,
  -- * accessors
  queryIsNull,
  queryIsAcct,
  queryIsAmt,
  queryIsDepth,
  queryIsDate,
  queryIsDate2,
  queryIsDateOrDate2,
  queryIsStartDateOnly,
  queryIsSym,
  queryIsReal,
  queryIsStatus,
  queryIsEmpty,
  queryStartDate,
  queryEndDate,
  queryDateSpan,
  queryDateSpan',
  queryDepth,
  inAccount,
  inAccountQuery,
  -- * matching
  matchesTransaction,
  matchesPosting,
  matchesAccount,
  matchesMixedAmount,
  matchesAmount,
  matchesCommodity,
  matchesPriceDirective,
  words'',
  -- * tests
  tests_Query
)
where

import Data.Data
import Data.Either
import Data.List
import Data.Maybe
#if !(MIN_VERSION_base(4,11,0))
import Data.Monoid ((<>))
#endif
import qualified Data.Text as T
import Data.Time.Calendar
import Safe (readDef, headDef)
import Text.Megaparsec
import Text.Megaparsec.Char

import Hledger.Utils hiding (words')
import Hledger.Data.Types
import Hledger.Data.AccountName
import Hledger.Data.Amount (nullamt, usd)
import Hledger.Data.Dates
import Hledger.Data.Posting
import Hledger.Data.Transaction


-- | A query is a composition of search criteria, which can be used to
-- match postings, transactions, accounts and more.
data Query = Any              -- ^ always match
           | None             -- ^ never match
           | Not Query        -- ^ negate this match
           | Or [Query]       -- ^ match if any of these match
           | And [Query]      -- ^ match if all of these match
           | Code Regexp      -- ^ match if code matches this regexp
           | Desc Regexp      -- ^ match if description matches this regexp
           | Acct Regexp      -- ^ match postings whose account matches this regexp
           | Date DateSpan    -- ^ match if primary date in this date span
           | Date2 DateSpan   -- ^ match if secondary date in this date span
           | StatusQ Status  -- ^ match txns/postings with this status
           | Real Bool        -- ^ match if "realness" (involves a real non-virtual account ?) has this value
           | Amt OrdPlus Quantity  -- ^ match if the amount's numeric quantity is less than/greater than/equal to/unsignedly equal to some value
           | Sym Regexp       -- ^ match if the entire commodity symbol is matched by this regexp
           | Empty Bool       -- ^ if true, show zero-amount postings/accounts which are usually not shown
                              --   more of a query option than a query criteria ?
           | Depth Int        -- ^ match if account depth is less than or equal to this value.
                              --   Depth is sometimes used like a query (for filtering report data)
                              --   and sometimes like a query option (for controlling display)
           | Tag Regexp (Maybe Regexp)  -- ^ match if a tag's name, and optionally its value, is matched by these respective regexps
                                        -- matching the regexp if provided, exists
    deriving (Query -> Query -> Bool
(Query -> Query -> Bool) -> (Query -> Query -> Bool) -> Eq Query
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Query -> Query -> Bool
$c/= :: Query -> Query -> Bool
== :: Query -> Query -> Bool
$c== :: Query -> Query -> Bool
Eq,Typeable Query
Constr
DataType
Typeable Query =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> Query -> c Query)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Query)
-> (Query -> Constr)
-> (Query -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Query))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Query))
-> ((forall b. Data b => b -> b) -> Query -> Query)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r)
-> (forall u. (forall d. Data d => d -> u) -> Query -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> Query -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> Query -> m Query)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Query -> m Query)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Query -> m Query)
-> Data Query
Query -> Constr
Query -> DataType
(forall b. Data b => b -> b) -> Query -> Query
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Query -> c Query
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Query
forall a.
Typeable a =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> Query -> u
forall u. (forall d. Data d => d -> u) -> Query -> [u]
forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r
forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Query -> m Query
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Query -> m Query
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Query
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Query -> c Query
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Query)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Query)
$cTag :: Constr
$cDepth :: Constr
$cEmpty :: Constr
$cSym :: Constr
$cAmt :: Constr
$cReal :: Constr
$cStatusQ :: Constr
$cDate2 :: Constr
$cDate :: Constr
$cAcct :: Constr
$cDesc :: Constr
$cCode :: Constr
$cAnd :: Constr
$cOr :: Constr
$cNot :: Constr
$cNone :: Constr
$cAny :: Constr
$tQuery :: DataType
gmapMo :: (forall d. Data d => d -> m d) -> Query -> m Query
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Query -> m Query
gmapMp :: (forall d. Data d => d -> m d) -> Query -> m Query
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Query -> m Query
gmapM :: (forall d. Data d => d -> m d) -> Query -> m Query
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Query -> m Query
gmapQi :: Int -> (forall d. Data d => d -> u) -> Query -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Query -> u
gmapQ :: (forall d. Data d => d -> u) -> Query -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> Query -> [u]
gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r
$cgmapQr :: forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r
gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r
$cgmapQl :: forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Query -> r
gmapT :: (forall b. Data b => b -> b) -> Query -> Query
$cgmapT :: (forall b. Data b => b -> b) -> Query -> Query
dataCast2 :: (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Query)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Query)
dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c Query)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Query)
dataTypeOf :: Query -> DataType
$cdataTypeOf :: Query -> DataType
toConstr :: Query -> Constr
$ctoConstr :: Query -> Constr
gunfold :: (forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Query
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Query
gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Query -> c Query
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Query -> c Query
$cp1Data :: Typeable Query
Data,Typeable)

-- custom Show implementation to show strings more accurately, eg for debugging regexps
instance Show Query where
  show :: Query -> String
show Any           = "Any"
  show None          = "None"
  show (Not q :: Query
q)       = "Not ("   String -> ShowS
forall a. [a] -> [a] -> [a]
++ Query -> String
forall a. Show a => a -> String
show Query
q  String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")"
  show (Or qs :: [Query]
qs)       = "Or ("    String -> ShowS
forall a. [a] -> [a] -> [a]
++ [Query] -> String
forall a. Show a => a -> String
show [Query]
qs String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")"
  show (And qs :: [Query]
qs)      = "And ("   String -> ShowS
forall a. [a] -> [a] -> [a]
++ [Query] -> String
forall a. Show a => a -> String
show [Query]
qs String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")"
  show (Code r :: String
r)      = "Code "   String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
r
  show (Desc r :: String
r)      = "Desc "   String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
r
  show (Acct r :: String
r)      = "Acct "   String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
r
  show (Date ds :: DateSpan
ds)     = "Date ("  String -> ShowS
forall a. [a] -> [a] -> [a]
++ DateSpan -> String
forall a. Show a => a -> String
show DateSpan
ds String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")"
  show (Date2 ds :: DateSpan
ds)    = "Date2 (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ DateSpan -> String
forall a. Show a => a -> String
show DateSpan
ds String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")"
  show (StatusQ b :: Status
b)    = "StatusQ " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Status -> String
forall a. Show a => a -> String
show Status
b
  show (Real b :: Bool
b)      = "Real "   String -> ShowS
forall a. [a] -> [a] -> [a]
++ Bool -> String
forall a. Show a => a -> String
show Bool
b
  show (Amt ord :: OrdPlus
ord qty :: Quantity
qty) = "Amt "    String -> ShowS
forall a. [a] -> [a] -> [a]
++ OrdPlus -> String
forall a. Show a => a -> String
show OrdPlus
ord String -> ShowS
forall a. [a] -> [a] -> [a]
++ " " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Quantity -> String
forall a. Show a => a -> String
show Quantity
qty
  show (Sym r :: String
r)       = "Sym "    String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
r
  show (Empty b :: Bool
b)     = "Empty "  String -> ShowS
forall a. [a] -> [a] -> [a]
++ Bool -> String
forall a. Show a => a -> String
show Bool
b
  show (Depth n :: Int
n)     = "Depth "  String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
n
  show (Tag s :: String
s ms :: Maybe String
ms)    = "Tag "    String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
s String -> ShowS
forall a. [a] -> [a] -> [a]
++ " (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Maybe String -> String
forall a. Show a => a -> String
show Maybe String
ms String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")"

-- | A more expressive Ord, used for amt: queries. The Abs* variants
-- compare with the absolute value of a number, ignoring sign.
data OrdPlus = Lt | LtEq | Gt | GtEq | Eq | AbsLt | AbsLtEq | AbsGt | AbsGtEq | AbsEq
 deriving (Int -> OrdPlus -> ShowS
[OrdPlus] -> ShowS
OrdPlus -> String
(Int -> OrdPlus -> ShowS)
-> (OrdPlus -> String) -> ([OrdPlus] -> ShowS) -> Show OrdPlus
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [OrdPlus] -> ShowS
$cshowList :: [OrdPlus] -> ShowS
show :: OrdPlus -> String
$cshow :: OrdPlus -> String
showsPrec :: Int -> OrdPlus -> ShowS
$cshowsPrec :: Int -> OrdPlus -> ShowS
Show,OrdPlus -> OrdPlus -> Bool
(OrdPlus -> OrdPlus -> Bool)
-> (OrdPlus -> OrdPlus -> Bool) -> Eq OrdPlus
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: OrdPlus -> OrdPlus -> Bool
$c/= :: OrdPlus -> OrdPlus -> Bool
== :: OrdPlus -> OrdPlus -> Bool
$c== :: OrdPlus -> OrdPlus -> Bool
Eq,Typeable OrdPlus
Constr
DataType
Typeable OrdPlus =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> OrdPlus -> c OrdPlus)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c OrdPlus)
-> (OrdPlus -> Constr)
-> (OrdPlus -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c OrdPlus))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c OrdPlus))
-> ((forall b. Data b => b -> b) -> OrdPlus -> OrdPlus)
-> (forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> OrdPlus -> r)
-> (forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> OrdPlus -> r)
-> (forall u. (forall d. Data d => d -> u) -> OrdPlus -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> OrdPlus -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus)
-> Data OrdPlus
OrdPlus -> Constr
OrdPlus -> DataType
(forall b. Data b => b -> b) -> OrdPlus -> OrdPlus
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> OrdPlus -> c OrdPlus
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c OrdPlus
forall a.
Typeable a =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> OrdPlus -> u
forall u. (forall d. Data d => d -> u) -> OrdPlus -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> OrdPlus -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> OrdPlus -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c OrdPlus
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> OrdPlus -> c OrdPlus
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c OrdPlus)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c OrdPlus)
$cAbsEq :: Constr
$cAbsGtEq :: Constr
$cAbsGt :: Constr
$cAbsLtEq :: Constr
$cAbsLt :: Constr
$cEq :: Constr
$cGtEq :: Constr
$cGt :: Constr
$cLtEq :: Constr
$cLt :: Constr
$tOrdPlus :: DataType
gmapMo :: (forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
gmapMp :: (forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
gmapM :: (forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> OrdPlus -> m OrdPlus
gmapQi :: Int -> (forall d. Data d => d -> u) -> OrdPlus -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> OrdPlus -> u
gmapQ :: (forall d. Data d => d -> u) -> OrdPlus -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> OrdPlus -> [u]
gmapQr :: (r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> OrdPlus -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> OrdPlus -> r
gmapQl :: (r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> OrdPlus -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> OrdPlus -> r
gmapT :: (forall b. Data b => b -> b) -> OrdPlus -> OrdPlus
$cgmapT :: (forall b. Data b => b -> b) -> OrdPlus -> OrdPlus
dataCast2 :: (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c OrdPlus)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c OrdPlus)
dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c OrdPlus)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c OrdPlus)
dataTypeOf :: OrdPlus -> DataType
$cdataTypeOf :: OrdPlus -> DataType
toConstr :: OrdPlus -> Constr
$ctoConstr :: OrdPlus -> Constr
gunfold :: (forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c OrdPlus
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c OrdPlus
gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> OrdPlus -> c OrdPlus
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> OrdPlus -> c OrdPlus
$cp1Data :: Typeable OrdPlus
Data,Typeable)

-- | A query option changes a query's/report's behaviour and output in some way.
data QueryOpt = QueryOptInAcctOnly AccountName  -- ^ show an account register focussed on this account
              | QueryOptInAcct AccountName      -- ^ as above but include sub-accounts in the account register
           -- | QueryOptCostBasis      -- ^ show amounts converted to cost where possible
           -- | QueryOptDate2  -- ^ show secondary dates instead of primary dates
    deriving (Int -> QueryOpt -> ShowS
[QueryOpt] -> ShowS
QueryOpt -> String
(Int -> QueryOpt -> ShowS)
-> (QueryOpt -> String) -> ([QueryOpt] -> ShowS) -> Show QueryOpt
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [QueryOpt] -> ShowS
$cshowList :: [QueryOpt] -> ShowS
show :: QueryOpt -> String
$cshow :: QueryOpt -> String
showsPrec :: Int -> QueryOpt -> ShowS
$cshowsPrec :: Int -> QueryOpt -> ShowS
Show, QueryOpt -> QueryOpt -> Bool
(QueryOpt -> QueryOpt -> Bool)
-> (QueryOpt -> QueryOpt -> Bool) -> Eq QueryOpt
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: QueryOpt -> QueryOpt -> Bool
$c/= :: QueryOpt -> QueryOpt -> Bool
== :: QueryOpt -> QueryOpt -> Bool
$c== :: QueryOpt -> QueryOpt -> Bool
Eq, Typeable QueryOpt
Constr
DataType
Typeable QueryOpt =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> QueryOpt -> c QueryOpt)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c QueryOpt)
-> (QueryOpt -> Constr)
-> (QueryOpt -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c QueryOpt))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c QueryOpt))
-> ((forall b. Data b => b -> b) -> QueryOpt -> QueryOpt)
-> (forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> QueryOpt -> r)
-> (forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> QueryOpt -> r)
-> (forall u. (forall d. Data d => d -> u) -> QueryOpt -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> QueryOpt -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt)
-> Data QueryOpt
QueryOpt -> Constr
QueryOpt -> DataType
(forall b. Data b => b -> b) -> QueryOpt -> QueryOpt
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> QueryOpt -> c QueryOpt
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c QueryOpt
forall a.
Typeable a =>
(forall (c :: * -> *).
 (forall d b. Data d => c (d -> b) -> d -> c b)
 -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> QueryOpt -> u
forall u. (forall d. Data d => d -> u) -> QueryOpt -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> QueryOpt -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> QueryOpt -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c QueryOpt
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> QueryOpt -> c QueryOpt
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c QueryOpt)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c QueryOpt)
$cQueryOptInAcct :: Constr
$cQueryOptInAcctOnly :: Constr
$tQueryOpt :: DataType
gmapMo :: (forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
gmapMp :: (forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
gmapM :: (forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> QueryOpt -> m QueryOpt
gmapQi :: Int -> (forall d. Data d => d -> u) -> QueryOpt -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> QueryOpt -> u
gmapQ :: (forall d. Data d => d -> u) -> QueryOpt -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> QueryOpt -> [u]
gmapQr :: (r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> QueryOpt -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> QueryOpt -> r
gmapQl :: (r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> QueryOpt -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> QueryOpt -> r
gmapT :: (forall b. Data b => b -> b) -> QueryOpt -> QueryOpt
$cgmapT :: (forall b. Data b => b -> b) -> QueryOpt -> QueryOpt
dataCast2 :: (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c QueryOpt)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c QueryOpt)
dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c QueryOpt)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c QueryOpt)
dataTypeOf :: QueryOpt -> DataType
$cdataTypeOf :: QueryOpt -> DataType
toConstr :: QueryOpt -> Constr
$ctoConstr :: QueryOpt -> Constr
gunfold :: (forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c QueryOpt
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c QueryOpt
gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> QueryOpt -> c QueryOpt
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> QueryOpt -> c QueryOpt
$cp1Data :: Typeable QueryOpt
Data, Typeable)

-- parsing

-- -- | A query restricting the account(s) to be shown in the sidebar, if any.
-- -- Just looks at the first query option.
-- showAccountMatcher :: [QueryOpt] -> Maybe Query
-- showAccountMatcher (QueryOptInAcctSubsOnly a:_) = Just $ Acct True $ accountNameToAccountRegex a
-- showAccountMatcher _ = Nothing


-- | Convert a query expression containing zero or more space-separated
-- terms to a query and zero or more query options. A query term is either:
--
-- 1. a search pattern, which matches on one or more fields, eg:
--
--      acct:REGEXP     - match the account name with a regular expression
--      desc:REGEXP     - match the transaction description
--      date:PERIODEXP  - match the date with a period expression
--
--    The prefix indicates the field to match, or if there is no prefix
--    account name is assumed.
--
-- 2. a query option, which modifies the reporting behaviour in some
--    way. There is currently one of these, which may appear only once:
--
--      inacct:FULLACCTNAME
--
-- The usual shell quoting rules are assumed. When a pattern contains
-- whitespace, it (or the whole term including prefix) should be enclosed
-- in single or double quotes.
--
-- Period expressions may contain relative dates, so a reference date is
-- required to fully parse these.
--
-- Multiple terms are combined as follows:
-- 1. multiple account patterns are OR'd together
-- 2. multiple description patterns are OR'd together
-- 3. multiple status patterns are OR'd together
-- 4. then all terms are AND'd together
parseQuery :: Day -> T.Text -> (Query,[QueryOpt])
parseQuery :: Day -> Text -> (Query, [QueryOpt])
parseQuery d :: Day
d s :: Text
s = (Query
q, [QueryOpt]
opts)
  where
    terms :: [Text]
terms = [Text] -> Text -> [Text]
words'' [Text]
prefixes Text
s
    (pats :: [Query]
pats, opts :: [QueryOpt]
opts) = [Either Query QueryOpt] -> ([Query], [QueryOpt])
forall a b. [Either a b] -> ([a], [b])
partitionEithers ([Either Query QueryOpt] -> ([Query], [QueryOpt]))
-> [Either Query QueryOpt] -> ([Query], [QueryOpt])
forall a b. (a -> b) -> a -> b
$ (Text -> Either Query QueryOpt)
-> [Text] -> [Either Query QueryOpt]
forall a b. (a -> b) -> [a] -> [b]
map (Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
d) [Text]
terms
    (descpats :: [Query]
descpats, pats' :: [Query]
pats') = (Query -> Bool) -> [Query] -> ([Query], [Query])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition Query -> Bool
queryIsDesc [Query]
pats
    (acctpats :: [Query]
acctpats, pats'' :: [Query]
pats'') = (Query -> Bool) -> [Query] -> ([Query], [Query])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition Query -> Bool
queryIsAcct [Query]
pats'
    (statuspats :: [Query]
statuspats, otherpats :: [Query]
otherpats) = (Query -> Bool) -> [Query] -> ([Query], [Query])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition Query -> Bool
queryIsStatus [Query]
pats''
    q :: Query
q = Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And ([Query] -> Query) -> [Query] -> Query
forall a b. (a -> b) -> a -> b
$ [[Query] -> Query
Or [Query]
acctpats, [Query] -> Query
Or [Query]
descpats, [Query] -> Query
Or [Query]
statuspats] [Query] -> [Query] -> [Query]
forall a. [a] -> [a] -> [a]
++ [Query]
otherpats

-- XXX
-- | Quote-and-prefix-aware version of words - don't split on spaces which
-- are inside quotes, including quotes which may have one of the specified
-- prefixes in front, and maybe an additional not: prefix in front of that.
words'' :: [T.Text] -> T.Text -> [T.Text]
words'' :: [Text] -> Text -> [Text]
words'' prefixes :: [Text]
prefixes = Either (ParseErrorBundle Text CustomErr) [Text] -> [Text]
forall t e a.
(Show t, Show (Token t), Show e) =>
Either (ParseErrorBundle t e) a -> a
fromparse (Either (ParseErrorBundle Text CustomErr) [Text] -> [Text])
-> (Text -> Either (ParseErrorBundle Text CustomErr) [Text])
-> Text
-> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parsec CustomErr Text [Text]
-> Text -> Either (ParseErrorBundle Text CustomErr) [Text]
forall e a.
Parsec e Text a -> Text -> Either (ParseErrorBundle Text e) a
parsewith Parsec CustomErr Text [Text]
maybeprefixedquotedphrases -- XXX
    where
      maybeprefixedquotedphrases :: SimpleTextParser [T.Text]
      maybeprefixedquotedphrases :: Parsec CustomErr Text [Text]
maybeprefixedquotedphrases = [TextParser Identity Text] -> TextParser Identity Text
forall (m :: * -> *) a. [TextParser m a] -> TextParser m a
choice' [TextParser Identity Text
prefixedQuotedPattern, TextParser Identity Text
singleQuotedPattern, TextParser Identity Text
doubleQuotedPattern, TextParser Identity Text
pattern] TextParser Identity Text
-> ParsecT CustomErr Text Identity ()
-> Parsec CustomErr Text [Text]
forall (m :: * -> *) a sep. MonadPlus m => m a -> m sep -> m [a]
`sepBy` ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity ()
forall (m :: * -> *) a. MonadPlus m => m a -> m ()
skipSome ParsecT CustomErr Text Identity Char
forall s (m :: * -> *).
(Stream s, Char ~ Token s) =>
ParsecT CustomErr s m Char
spacenonewline
      prefixedQuotedPattern :: SimpleTextParser T.Text
      prefixedQuotedPattern :: TextParser Identity Text
prefixedQuotedPattern = do
        Text
not' <- Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe Text -> Text)
-> ParsecT CustomErr Text Identity (Maybe Text)
-> TextParser Identity Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` (TextParser Identity Text
-> ParsecT CustomErr Text Identity (Maybe Text)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional (TextParser Identity Text
 -> ParsecT CustomErr Text Identity (Maybe Text))
-> TextParser Identity Text
-> ParsecT CustomErr Text Identity (Maybe Text)
forall a b. (a -> b) -> a -> b
$ Tokens Text -> ParsecT CustomErr Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
string "not:")
        let allowednexts :: [Text]
allowednexts | Text -> Bool
T.null Text
not' = [Text]
prefixes
                         | Bool
otherwise   = [Text]
prefixes [Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [""]
        Text
next <- [TextParser Identity Text] -> TextParser Identity Text
forall (m :: * -> *) a. [TextParser m a] -> TextParser m a
choice' ([TextParser Identity Text] -> TextParser Identity Text)
-> [TextParser Identity Text] -> TextParser Identity Text
forall a b. (a -> b) -> a -> b
$ (Text -> TextParser Identity Text)
-> [Text] -> [TextParser Identity Text]
forall a b. (a -> b) -> [a] -> [b]
map Text -> TextParser Identity Text
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
string [Text]
allowednexts
        let prefix :: T.Text
            prefix :: Text
prefix = Text
not' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
next
        Text
p <- TextParser Identity Text
singleQuotedPattern TextParser Identity Text
-> TextParser Identity Text -> TextParser Identity Text
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> TextParser Identity Text
doubleQuotedPattern
        Text -> TextParser Identity Text
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> TextParser Identity Text)
-> Text -> TextParser Identity Text
forall a b. (a -> b) -> a -> b
$ Text
prefix Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
stripquotes Text
p
      singleQuotedPattern :: SimpleTextParser T.Text
      singleQuotedPattern :: TextParser Identity Text
singleQuotedPattern = ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity String
-> ParsecT CustomErr Text Identity String
forall (m :: * -> *) open close a.
Applicative m =>
m open -> m close -> m a -> m a
between (Token Text -> ParsecT CustomErr Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Token Text
'\'') (Token Text -> ParsecT CustomErr Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Token Text
'\'') (ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many (ParsecT CustomErr Text Identity Char
 -> ParsecT CustomErr Text Identity String)
-> ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity String
forall a b. (a -> b) -> a -> b
$ [Token Text] -> ParsecT CustomErr Text Identity (Token Text)
forall (f :: * -> *) e s (m :: * -> *).
(Foldable f, MonadParsec e s m) =>
f (Token s) -> m (Token s)
noneOf ("'" :: [Char])) ParsecT CustomErr Text Identity String
-> (String -> TextParser Identity Text) -> TextParser Identity Text
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> TextParser Identity Text
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> TextParser Identity Text)
-> (String -> Text) -> String -> TextParser Identity Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
stripquotes (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack
      doubleQuotedPattern :: SimpleTextParser T.Text
      doubleQuotedPattern :: TextParser Identity Text
doubleQuotedPattern = ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity String
-> ParsecT CustomErr Text Identity String
forall (m :: * -> *) open close a.
Applicative m =>
m open -> m close -> m a -> m a
between (Token Text -> ParsecT CustomErr Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Token Text
'"') (Token Text -> ParsecT CustomErr Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Token Text
'"') (ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many (ParsecT CustomErr Text Identity Char
 -> ParsecT CustomErr Text Identity String)
-> ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity String
forall a b. (a -> b) -> a -> b
$ [Token Text] -> ParsecT CustomErr Text Identity (Token Text)
forall (f :: * -> *) e s (m :: * -> *).
(Foldable f, MonadParsec e s m) =>
f (Token s) -> m (Token s)
noneOf ("\"" :: [Char])) ParsecT CustomErr Text Identity String
-> (String -> TextParser Identity Text) -> TextParser Identity Text
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> TextParser Identity Text
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> TextParser Identity Text)
-> (String -> Text) -> String -> TextParser Identity Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
stripquotes (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack
      pattern :: SimpleTextParser T.Text
      pattern :: TextParser Identity Text
pattern = (String -> Text)
-> ParsecT CustomErr Text Identity String
-> TextParser Identity Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> Text
T.pack (ParsecT CustomErr Text Identity String
 -> TextParser Identity Text)
-> ParsecT CustomErr Text Identity String
-> TextParser Identity Text
forall a b. (a -> b) -> a -> b
$ ParsecT CustomErr Text Identity Char
-> ParsecT CustomErr Text Identity String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many ([Token Text] -> ParsecT CustomErr Text Identity (Token Text)
forall (f :: * -> *) e s (m :: * -> *).
(Foldable f, MonadParsec e s m) =>
f (Token s) -> m (Token s)
noneOf (" \n\r" :: [Char]))

-- XXX
-- keep synced with patterns below, excluding "not"
prefixes :: [T.Text]
prefixes :: [Text]
prefixes = (Text -> Text) -> [Text] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>":") [
     "inacctonly"
    ,"inacct"
    ,"amt"
    ,"code"
    ,"desc"
    ,"payee"
    ,"note"
    ,"acct"
    ,"date"
    ,"date2"
    ,"status"
    ,"cur"
    ,"real"
    ,"empty"
    ,"depth"
    ,"tag"
    ]

defaultprefix :: T.Text
defaultprefix :: Text
defaultprefix = "acct"

-- -- | Parse the query string as a boolean tree of match patterns.
-- parseQueryTerm :: String -> Query
-- parseQueryTerm s = either (const (Any)) id $ runParser query () "" $ lexmatcher s

-- lexmatcher :: String -> [String]
-- lexmatcher s = words' s

-- query :: GenParser String () Query
-- query = undefined

-- | Parse a single query term as either a query or a query option,
-- or raise an error if it has invalid syntax.
parseQueryTerm :: Day -> T.Text -> Either Query QueryOpt
parseQueryTerm :: Day -> Text -> Either Query QueryOpt
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "inacctonly:" -> Just s :: Text
s) = QueryOpt -> Either Query QueryOpt
forall a b. b -> Either a b
Right (QueryOpt -> Either Query QueryOpt)
-> QueryOpt -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Text -> QueryOpt
QueryOptInAcctOnly Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "inacct:" -> Just s :: Text
s) = QueryOpt -> Either Query QueryOpt
forall a b. b -> Either a b
Right (QueryOpt -> Either Query QueryOpt)
-> QueryOpt -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Text -> QueryOpt
QueryOptInAcct Text
s
parseQueryTerm d :: Day
d (Text -> Text -> Maybe Text
T.stripPrefix "not:" -> Just s :: Text
s) =
  case Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
d Text
s of
    Left m :: Query
m -> Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Query -> Query
Not Query
m
    Right _ -> Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left Query
Any -- not:somequeryoption will be ignored
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "code:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Query
Code (String -> Query) -> String -> Query
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "desc:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Query
Desc (String -> Query) -> String -> Query
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "payee:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> Query
Tag "payee" (Maybe String -> Query) -> Maybe String -> Query
forall a b. (a -> b) -> a -> b
$ String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "note:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> Query
Tag "note" (Maybe String -> Query) -> Maybe String -> Query
forall a b. (a -> b) -> a -> b
$ String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "acct:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Query
Acct (String -> Query) -> String -> Query
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s
parseQueryTerm d :: Day
d (Text -> Text -> Maybe Text
T.stripPrefix "date2:" -> Just s :: Text
s) =
        case Day
-> Text
-> Either (ParseErrorBundle Text CustomErr) (Interval, DateSpan)
parsePeriodExpr Day
d Text
s of Left e :: ParseErrorBundle Text CustomErr
e         -> String -> Either Query QueryOpt
forall a. String -> a
error' (String -> Either Query QueryOpt)
-> String -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ "\"date2:"String -> ShowS
forall a. [a] -> [a] -> [a]
++Text -> String
T.unpack Text
sString -> ShowS
forall a. [a] -> [a] -> [a]
++"\" gave a "String -> ShowS
forall a. [a] -> [a] -> [a]
++ParseErrorBundle Text CustomErr -> String
forall t e.
(Show t, Show (Token t), Show e) =>
ParseErrorBundle t e -> String
showDateParseError ParseErrorBundle Text CustomErr
e
                                    Right (_,span :: DateSpan
span) -> Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
Date2 DateSpan
span
parseQueryTerm d :: Day
d (Text -> Text -> Maybe Text
T.stripPrefix "date:" -> Just s :: Text
s) =
        case Day
-> Text
-> Either (ParseErrorBundle Text CustomErr) (Interval, DateSpan)
parsePeriodExpr Day
d Text
s of Left e :: ParseErrorBundle Text CustomErr
e         -> String -> Either Query QueryOpt
forall a. String -> a
error' (String -> Either Query QueryOpt)
-> String -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ "\"date:"String -> ShowS
forall a. [a] -> [a] -> [a]
++Text -> String
T.unpack Text
sString -> ShowS
forall a. [a] -> [a] -> [a]
++"\" gave a "String -> ShowS
forall a. [a] -> [a] -> [a]
++ParseErrorBundle Text CustomErr -> String
forall t e.
(Show t, Show (Token t), Show e) =>
ParseErrorBundle t e -> String
showDateParseError ParseErrorBundle Text CustomErr
e
                                    Right (_,span :: DateSpan
span) -> Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
Date DateSpan
span
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "status:" -> Just s :: Text
s) =
        case Text -> Either String Status
parseStatus Text
s of Left e :: String
e   -> String -> Either Query QueryOpt
forall a. String -> a
error' (String -> Either Query QueryOpt)
-> String -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ "\"status:"String -> ShowS
forall a. [a] -> [a] -> [a]
++Text -> String
T.unpack Text
sString -> ShowS
forall a. [a] -> [a] -> [a]
++"\" gave a parse error: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
e
                              Right st :: Status
st -> Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
st
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "real:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Bool -> Query
Real (Bool -> Query) -> Bool -> Query
forall a b. (a -> b) -> a -> b
$ Text -> Bool
parseBool Text
s Bool -> Bool -> Bool
|| Text -> Bool
T.null Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "amt:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ OrdPlus -> Quantity -> Query
Amt OrdPlus
ord Quantity
q where (ord :: OrdPlus
ord, q :: Quantity
q) = Text -> (OrdPlus, Quantity)
parseAmountQueryTerm Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "empty:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Bool -> Query
Empty (Bool -> Query) -> Bool -> Query
forall a b. (a -> b) -> a -> b
$ Text -> Bool
parseBool Text
s
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "depth:" -> Just s :: Text
s)
  | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= 0    = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Int -> Query
Depth Int
n
  | Bool
otherwise = String -> Either Query QueryOpt
forall a. String -> a
error' "depth: should have a positive number"
  where n :: Int
n = Int -> String -> Int
forall a. Read a => a -> String -> a
readDef 0 (Text -> String
T.unpack Text
s)

parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "cur:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Query
Sym (Text -> String
T.unpack Text
s) -- support cur: as an alias
parseQueryTerm _ (Text -> Text -> Maybe Text
T.stripPrefix "tag:" -> Just s :: Text
s) = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> Query
Tag String
n Maybe String
v where (n :: String
n,v :: Maybe String
v) = Text -> (String, Maybe String)
parseTag Text
s
parseQueryTerm _ "" = Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Query
Any
parseQueryTerm d :: Day
d s :: Text
s = Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
d (Text -> Either Query QueryOpt) -> Text -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Text
defaultprefixText -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>":"Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>Text
s

-- | Parse what comes after amt: .
parseAmountQueryTerm :: T.Text -> (OrdPlus, Quantity)
parseAmountQueryTerm :: Text -> (OrdPlus, Quantity)
parseAmountQueryTerm s' :: Text
s' =
  case Text
s' of
    -- feel free to do this a smarter way
    ""              -> (OrdPlus, Quantity)
forall a. a
err
    (Text -> Text -> Maybe Text
T.stripPrefix "<+" -> Just s :: Text
s)  -> (OrdPlus
Lt, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "<=+" -> Just s :: Text
s) -> (OrdPlus
LtEq, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix ">+" -> Just s :: Text
s)  -> (OrdPlus
Gt, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix ">=+" -> Just s :: Text
s) -> (OrdPlus
GtEq, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "=+" -> Just s :: Text
s)  -> (OrdPlus
Eq, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "+" -> Just s :: Text
s)   -> (OrdPlus
Eq, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "<-" -> Just s :: Text
s)  -> (OrdPlus
Lt, Quantity -> Quantity
forall a. Num a => a -> a
negate (Quantity -> Quantity) -> Quantity -> Quantity
forall a b. (a -> b) -> a -> b
$ Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "<=-" -> Just s :: Text
s) -> (OrdPlus
LtEq, Quantity -> Quantity
forall a. Num a => a -> a
negate (Quantity -> Quantity) -> Quantity -> Quantity
forall a b. (a -> b) -> a -> b
$ Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix ">-" -> Just s :: Text
s)  -> (OrdPlus
Gt, Quantity -> Quantity
forall a. Num a => a -> a
negate (Quantity -> Quantity) -> Quantity -> Quantity
forall a b. (a -> b) -> a -> b
$ Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix ">=-" -> Just s :: Text
s) -> (OrdPlus
GtEq, Quantity -> Quantity
forall a. Num a => a -> a
negate (Quantity -> Quantity) -> Quantity -> Quantity
forall a b. (a -> b) -> a -> b
$ Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "=-" -> Just s :: Text
s)  -> (OrdPlus
Eq, Quantity -> Quantity
forall a. Num a => a -> a
negate (Quantity -> Quantity) -> Quantity -> Quantity
forall a b. (a -> b) -> a -> b
$ Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "-" -> Just s :: Text
s)   -> (OrdPlus
Eq, Quantity -> Quantity
forall a. Num a => a -> a
negate (Quantity -> Quantity) -> Quantity -> Quantity
forall a b. (a -> b) -> a -> b
$ Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    (Text -> Text -> Maybe Text
T.stripPrefix "<=" -> Just s :: Text
s)  -> let n :: Quantity
n = Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s) in
                                         case Quantity
n of
                                           0 -> (OrdPlus
LtEq, 0)
                                           _ -> (OrdPlus
AbsLtEq, Quantity
n)
    (Text -> Text -> Maybe Text
T.stripPrefix "<" -> Just s :: Text
s)   -> let n :: Quantity
n = Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s) in
                                         case Quantity
n of 0 -> (OrdPlus
Lt, 0)
                                                   _ -> (OrdPlus
AbsLt, Quantity
n)
    (Text -> Text -> Maybe Text
T.stripPrefix ">=" -> Just s :: Text
s)  -> let n :: Quantity
n = Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s) in
                                         case Quantity
n of 0 -> (OrdPlus
GtEq, 0)
                                                   _ -> (OrdPlus
AbsGtEq, Quantity
n)
    (Text -> Text -> Maybe Text
T.stripPrefix ">" -> Just s :: Text
s)   -> let n :: Quantity
n = Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s) in
                                         case Quantity
n of 0 -> (OrdPlus
Gt, 0)
                                                   _ -> (OrdPlus
AbsGt, Quantity
n)
    (Text -> Text -> Maybe Text
T.stripPrefix "=" -> Just s :: Text
s)           -> (OrdPlus
AbsEq, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
    s :: Text
s               -> (OrdPlus
AbsEq, Quantity -> String -> Quantity
forall a. Read a => a -> String -> a
readDef Quantity
forall a. a
err (Text -> String
T.unpack Text
s))
  where
    err :: a
err = String -> a
forall a. String -> a
error' (String -> a) -> String -> a
forall a b. (a -> b) -> a -> b
$ "could not parse as '=', '<', or '>' (optional) followed by a (optionally signed) numeric quantity: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
T.unpack Text
s'

parseTag :: T.Text -> (Regexp, Maybe Regexp)
parseTag :: Text -> (String, Maybe String)
parseTag s :: Text
s | "=" Text -> Text -> Bool
`T.isInfixOf` Text
s = (Text -> String
T.unpack Text
n, String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ ShowS
forall a. [a] -> [a]
tail ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
v)
           | Bool
otherwise    = (Text -> String
T.unpack Text
s, Maybe String
forall a. Maybe a
Nothing)
           where (n :: Text
n,v :: Text
v) = (Char -> Bool) -> Text -> (Text, Text)
T.break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
=='=') Text
s

-- | Parse the value part of a "status:" query, or return an error.
parseStatus :: T.Text -> Either String Status
parseStatus :: Text -> Either String Status
parseStatus s :: Text
s | Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["*","1"] = Status -> Either String Status
forall a b. b -> Either a b
Right Status
Cleared
              | Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["!"]     = Status -> Either String Status
forall a b. b -> Either a b
Right Status
Pending
              | Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["","0"]  = Status -> Either String Status
forall a b. b -> Either a b
Right Status
Unmarked
              | Bool
otherwise          = String -> Either String Status
forall a b. a -> Either a b
Left (String -> Either String Status) -> String -> Either String Status
forall a b. (a -> b) -> a -> b
$ "could not parse "String -> ShowS
forall a. [a] -> [a] -> [a]
++Text -> String
forall a. Show a => a -> String
show Text
sString -> ShowS
forall a. [a] -> [a] -> [a]
++" as a status (should be *, ! or empty)"

-- | Parse the boolean value part of a "status:" query. "1" means true,
-- anything else will be parsed as false without error.
parseBool :: T.Text -> Bool
parseBool :: Text -> Bool
parseBool s :: Text
s = Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
truestrings

truestrings :: [T.Text]
truestrings :: [Text]
truestrings = ["1"]

simplifyQuery :: Query -> Query
simplifyQuery :: Query -> Query
simplifyQuery q :: Query
q =
  let q' :: Query
q' = Query -> Query
simplify Query
q
  in if Query
q' Query -> Query -> Bool
forall a. Eq a => a -> a -> Bool
== Query
q then Query
q else Query -> Query
simplifyQuery Query
q'
  where
    simplify :: Query -> Query
simplify (And []) = Query
Any
    simplify (And [q :: Query
q]) = Query -> Query
simplify Query
q
    simplify (And qs :: [Query]
qs) | [Query] -> Bool
forall a. Eq a => [a] -> Bool
same [Query]
qs = Query -> Query
simplify (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
forall a. [a] -> a
head [Query]
qs
                      | (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> Query -> Bool
forall a. Eq a => a -> a -> Bool
==Query
None) [Query]
qs = Query
None
                      | (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Query -> Bool
queryIsDate [Query]
qs = DateSpan -> Query
Date (DateSpan -> Query) -> DateSpan -> Query
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> DateSpan
spansIntersect ([DateSpan] -> DateSpan) -> [DateSpan] -> DateSpan
forall a b. (a -> b) -> a -> b
$ (Query -> Maybe DateSpan) -> [Query] -> [DateSpan]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Query -> Maybe DateSpan
queryTermDateSpan [Query]
qs
                      | Bool
otherwise = [Query] -> Query
And ([Query] -> Query) -> [Query] -> Query
forall a b. (a -> b) -> a -> b
$ [[Query]] -> [Query]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Query]] -> [Query]) -> [[Query]] -> [Query]
forall a b. (a -> b) -> a -> b
$ [(Query -> Query) -> [Query] -> [Query]
forall a b. (a -> b) -> [a] -> [b]
map Query -> Query
simplify [Query]
dateqs, (Query -> Query) -> [Query] -> [Query]
forall a b. (a -> b) -> [a] -> [b]
map Query -> Query
simplify [Query]
otherqs]
                      where (dateqs :: [Query]
dateqs, otherqs :: [Query]
otherqs) = (Query -> Bool) -> [Query] -> ([Query], [Query])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition Query -> Bool
queryIsDate ([Query] -> ([Query], [Query])) -> [Query] -> ([Query], [Query])
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> [Query] -> [Query]
forall a. (a -> Bool) -> [a] -> [a]
filter (Query -> Query -> Bool
forall a. Eq a => a -> a -> Bool
/=Query
Any) [Query]
qs
    simplify (Or []) = Query
Any
    simplify (Or [q :: Query
q]) = Query -> Query
simplifyQuery Query
q
    simplify (Or qs :: [Query]
qs) | [Query] -> Bool
forall a. Eq a => [a] -> Bool
same [Query]
qs = Query -> Query
simplify (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
forall a. [a] -> a
head [Query]
qs
                     | (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> Query -> Bool
forall a. Eq a => a -> a -> Bool
==Query
Any) [Query]
qs = Query
Any
                     -- all queryIsDate qs = Date $ spansUnion $ mapMaybe queryTermDateSpan qs  ?
                     | Bool
otherwise = [Query] -> Query
Or ([Query] -> Query) -> [Query] -> Query
forall a b. (a -> b) -> a -> b
$ (Query -> Query) -> [Query] -> [Query]
forall a b. (a -> b) -> [a] -> [b]
map Query -> Query
simplify ([Query] -> [Query]) -> [Query] -> [Query]
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> [Query] -> [Query]
forall a. (a -> Bool) -> [a] -> [a]
filter (Query -> Query -> Bool
forall a. Eq a => a -> a -> Bool
/=Query
None) [Query]
qs
    simplify (Date (DateSpan Nothing Nothing)) = Query
Any
    simplify (Date2 (DateSpan Nothing Nothing)) = Query
Any
    simplify q :: Query
q = Query
q

same :: [a] -> Bool
same [] = Bool
True
same (a :: a
a:as :: [a]
as) = (a -> Bool) -> [a] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (a
aa -> a -> Bool
forall a. Eq a => a -> a -> Bool
==) [a]
as

-- | Remove query terms (or whole sub-expressions) not matching the given
-- predicate from this query.  XXX Semantics not completely clear.
filterQuery :: (Query -> Bool) -> Query -> Query
filterQuery :: (Query -> Bool) -> Query -> Query
filterQuery p :: Query -> Bool
p = Query -> Query
simplifyQuery (Query -> Query) -> (Query -> Query) -> Query -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery' Query -> Bool
p

filterQuery' :: (Query -> Bool) -> Query -> Query
filterQuery' :: (Query -> Bool) -> Query -> Query
filterQuery' p :: Query -> Bool
p (And qs :: [Query]
qs) = [Query] -> Query
And ([Query] -> Query) -> [Query] -> Query
forall a b. (a -> b) -> a -> b
$ (Query -> Query) -> [Query] -> [Query]
forall a b. (a -> b) -> [a] -> [b]
map ((Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
p) [Query]
qs
filterQuery' p :: Query -> Bool
p (Or qs :: [Query]
qs) = [Query] -> Query
Or ([Query] -> Query) -> [Query] -> Query
forall a b. (a -> b) -> a -> b
$ (Query -> Query) -> [Query] -> [Query]
forall a b. (a -> b) -> [a] -> [b]
map ((Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
p) [Query]
qs
-- filterQuery' p (Not q) = Not $ filterQuery p q
filterQuery' p :: Query -> Bool
p q :: Query
q = if Query -> Bool
p Query
q then Query
q else Query
Any

-- * accessors

-- | Does this query match everything ?
queryIsNull :: Query -> Bool
queryIsNull :: Query -> Bool
queryIsNull Any = Bool
True
queryIsNull (And []) = Bool
True
queryIsNull (Not (Or [])) = Bool
True
queryIsNull _ = Bool
False

queryIsDepth :: Query -> Bool
queryIsDepth :: Query -> Bool
queryIsDepth (Depth _) = Bool
True
queryIsDepth _ = Bool
False

queryIsDate :: Query -> Bool
queryIsDate :: Query -> Bool
queryIsDate (Date _) = Bool
True
queryIsDate _ = Bool
False

queryIsDate2 :: Query -> Bool
queryIsDate2 :: Query -> Bool
queryIsDate2 (Date2 _) = Bool
True
queryIsDate2 _ = Bool
False

queryIsDateOrDate2 :: Query -> Bool
queryIsDateOrDate2 :: Query -> Bool
queryIsDateOrDate2 (Date _) = Bool
True
queryIsDateOrDate2 (Date2 _) = Bool
True
queryIsDateOrDate2 _ = Bool
False

queryIsDesc :: Query -> Bool
queryIsDesc :: Query -> Bool
queryIsDesc (Desc _) = Bool
True
queryIsDesc _ = Bool
False

queryIsAcct :: Query -> Bool
queryIsAcct :: Query -> Bool
queryIsAcct (Acct _) = Bool
True
queryIsAcct _ = Bool
False

queryIsAmt :: Query -> Bool
queryIsAmt :: Query -> Bool
queryIsAmt (Amt _ _) = Bool
True
queryIsAmt _         = Bool
False

queryIsSym :: Query -> Bool
queryIsSym :: Query -> Bool
queryIsSym (Sym _) = Bool
True
queryIsSym _ = Bool
False

queryIsReal :: Query -> Bool
queryIsReal :: Query -> Bool
queryIsReal (Real _) = Bool
True
queryIsReal _ = Bool
False

queryIsStatus :: Query -> Bool
queryIsStatus :: Query -> Bool
queryIsStatus (StatusQ _) = Bool
True
queryIsStatus _ = Bool
False

queryIsEmpty :: Query -> Bool
queryIsEmpty :: Query -> Bool
queryIsEmpty (Empty _) = Bool
True
queryIsEmpty _ = Bool
False

-- | Does this query specify a start date and nothing else (that would
-- filter postings prior to the date) ?
-- When the flag is true, look for a starting secondary date instead.
queryIsStartDateOnly :: Bool -> Query -> Bool
queryIsStartDateOnly :: Bool -> Query -> Bool
queryIsStartDateOnly _ Any = Bool
False
queryIsStartDateOnly _ None = Bool
False
queryIsStartDateOnly secondary :: Bool
secondary (Or ms :: [Query]
ms) = [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and ([Bool] -> Bool) -> [Bool] -> Bool
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> [Query] -> [Bool]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> Bool
queryIsStartDateOnly Bool
secondary) [Query]
ms
queryIsStartDateOnly secondary :: Bool
secondary (And ms :: [Query]
ms) = [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and ([Bool] -> Bool) -> [Bool] -> Bool
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> [Query] -> [Bool]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> Bool
queryIsStartDateOnly Bool
secondary) [Query]
ms
queryIsStartDateOnly False (Date (DateSpan (Just _) _)) = Bool
True
queryIsStartDateOnly True (Date2 (DateSpan (Just _) _)) = Bool
True
queryIsStartDateOnly _ _ = Bool
False

-- | What start date (or secondary date) does this query specify, if any ?
-- For OR expressions, use the earliest of the dates. NOT is ignored.
queryStartDate :: Bool -> Query -> Maybe Day
queryStartDate :: Bool -> Query -> Maybe Day
queryStartDate secondary :: Bool
secondary (Or ms :: [Query]
ms) = [Maybe Day] -> Maybe Day
earliestMaybeDate ([Maybe Day] -> Maybe Day) -> [Maybe Day] -> Maybe Day
forall a b. (a -> b) -> a -> b
$ (Query -> Maybe Day) -> [Query] -> [Maybe Day]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> Maybe Day
queryStartDate Bool
secondary) [Query]
ms
queryStartDate secondary :: Bool
secondary (And ms :: [Query]
ms) = [Maybe Day] -> Maybe Day
latestMaybeDate ([Maybe Day] -> Maybe Day) -> [Maybe Day] -> Maybe Day
forall a b. (a -> b) -> a -> b
$ (Query -> Maybe Day) -> [Query] -> [Maybe Day]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> Maybe Day
queryStartDate Bool
secondary) [Query]
ms
queryStartDate False (Date (DateSpan (Just d :: Day
d) _)) = Day -> Maybe Day
forall a. a -> Maybe a
Just Day
d
queryStartDate True (Date2 (DateSpan (Just d :: Day
d) _)) = Day -> Maybe Day
forall a. a -> Maybe a
Just Day
d
queryStartDate _ _ = Maybe Day
forall a. Maybe a
Nothing

-- | What end date (or secondary date) does this query specify, if any ?
-- For OR expressions, use the latest of the dates. NOT is ignored.
queryEndDate :: Bool -> Query -> Maybe Day
queryEndDate :: Bool -> Query -> Maybe Day
queryEndDate secondary :: Bool
secondary (Or ms :: [Query]
ms) = [Maybe Day] -> Maybe Day
latestMaybeDate' ([Maybe Day] -> Maybe Day) -> [Maybe Day] -> Maybe Day
forall a b. (a -> b) -> a -> b
$ (Query -> Maybe Day) -> [Query] -> [Maybe Day]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> Maybe Day
queryEndDate Bool
secondary) [Query]
ms
queryEndDate secondary :: Bool
secondary (And ms :: [Query]
ms) = [Maybe Day] -> Maybe Day
earliestMaybeDate' ([Maybe Day] -> Maybe Day) -> [Maybe Day] -> Maybe Day
forall a b. (a -> b) -> a -> b
$ (Query -> Maybe Day) -> [Query] -> [Maybe Day]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> Maybe Day
queryEndDate Bool
secondary) [Query]
ms
queryEndDate False (Date (DateSpan _ (Just d :: Day
d))) = Day -> Maybe Day
forall a. a -> Maybe a
Just Day
d
queryEndDate True (Date2 (DateSpan _ (Just d :: Day
d))) = Day -> Maybe Day
forall a. a -> Maybe a
Just Day
d
queryEndDate _ _ = Maybe Day
forall a. Maybe a
Nothing

queryTermDateSpan :: Query -> Maybe DateSpan
queryTermDateSpan (Date span :: DateSpan
span) = DateSpan -> Maybe DateSpan
forall a. a -> Maybe a
Just DateSpan
span
queryTermDateSpan _ = Maybe DateSpan
forall a. Maybe a
Nothing

-- | What date span (or with a true argument, what secondary date span) does this query specify ?
-- OR clauses specifying multiple spans return their union (the span enclosing all of them).
-- AND clauses specifying multiple spans return their intersection.
-- NOT clauses are ignored.
queryDateSpan :: Bool -> Query -> DateSpan
queryDateSpan :: Bool -> Query -> DateSpan
queryDateSpan secondary :: Bool
secondary (Or qs :: [Query]
qs)  = [DateSpan] -> DateSpan
spansUnion     ([DateSpan] -> DateSpan) -> [DateSpan] -> DateSpan
forall a b. (a -> b) -> a -> b
$ (Query -> DateSpan) -> [Query] -> [DateSpan]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> DateSpan
queryDateSpan Bool
secondary) [Query]
qs
queryDateSpan secondary :: Bool
secondary (And qs :: [Query]
qs) = [DateSpan] -> DateSpan
spansIntersect ([DateSpan] -> DateSpan) -> [DateSpan] -> DateSpan
forall a b. (a -> b) -> a -> b
$ (Query -> DateSpan) -> [Query] -> [DateSpan]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Query -> DateSpan
queryDateSpan Bool
secondary) [Query]
qs
queryDateSpan False (Date span :: DateSpan
span)  = DateSpan
span
queryDateSpan True (Date2 span :: DateSpan
span)  = DateSpan
span
queryDateSpan _ _                = DateSpan
nulldatespan

-- | What date span does this query specify, treating primary and secondary dates as equivalent ?
-- OR clauses specifying multiple spans return their union (the span enclosing all of them).
-- AND clauses specifying multiple spans return their intersection.
-- NOT clauses are ignored.
queryDateSpan' :: Query -> DateSpan
queryDateSpan' :: Query -> DateSpan
queryDateSpan' (Or qs :: [Query]
qs)      = [DateSpan] -> DateSpan
spansUnion     ([DateSpan] -> DateSpan) -> [DateSpan] -> DateSpan
forall a b. (a -> b) -> a -> b
$ (Query -> DateSpan) -> [Query] -> [DateSpan]
forall a b. (a -> b) -> [a] -> [b]
map Query -> DateSpan
queryDateSpan' [Query]
qs
queryDateSpan' (And qs :: [Query]
qs)     = [DateSpan] -> DateSpan
spansIntersect ([DateSpan] -> DateSpan) -> [DateSpan] -> DateSpan
forall a b. (a -> b) -> a -> b
$ (Query -> DateSpan) -> [Query] -> [DateSpan]
forall a b. (a -> b) -> [a] -> [b]
map Query -> DateSpan
queryDateSpan' [Query]
qs
queryDateSpan' (Date span :: DateSpan
span)  = DateSpan
span
queryDateSpan' (Date2 span :: DateSpan
span) = DateSpan
span
queryDateSpan' _            = DateSpan
nulldatespan

-- | What is the earliest of these dates, where Nothing is latest ?
earliestMaybeDate :: [Maybe Day] -> Maybe Day
earliestMaybeDate :: [Maybe Day] -> Maybe Day
earliestMaybeDate mds :: [Maybe Day]
mds = [Maybe Day] -> Maybe Day
forall a. [a] -> a
head ([Maybe Day] -> Maybe Day) -> [Maybe Day] -> Maybe Day
forall a b. (a -> b) -> a -> b
$ (Maybe Day -> Maybe Day -> Ordering) -> [Maybe Day] -> [Maybe Day]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy Maybe Day -> Maybe Day -> Ordering
compareMaybeDates [Maybe Day]
mds [Maybe Day] -> [Maybe Day] -> [Maybe Day]
forall a. [a] -> [a] -> [a]
++ [Maybe Day
forall a. Maybe a
Nothing]

-- | What is the latest of these dates, where Nothing is earliest ?
latestMaybeDate :: [Maybe Day] -> Maybe Day
latestMaybeDate :: [Maybe Day] -> Maybe Day
latestMaybeDate = Maybe Day -> [Maybe Day] -> Maybe Day
forall a. a -> [a] -> a
headDef Maybe Day
forall a. Maybe a
Nothing ([Maybe Day] -> Maybe Day)
-> ([Maybe Day] -> [Maybe Day]) -> [Maybe Day] -> Maybe Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Day -> Maybe Day -> Ordering) -> [Maybe Day] -> [Maybe Day]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy ((Maybe Day -> Maybe Day -> Ordering)
-> Maybe Day -> Maybe Day -> Ordering
forall a b c. (a -> b -> c) -> b -> a -> c
flip Maybe Day -> Maybe Day -> Ordering
compareMaybeDates)

-- | What is the earliest of these dates, ignoring Nothings ?
earliestMaybeDate' :: [Maybe Day] -> Maybe Day
earliestMaybeDate' :: [Maybe Day] -> Maybe Day
earliestMaybeDate' = Maybe Day -> [Maybe Day] -> Maybe Day
forall a. a -> [a] -> a
headDef Maybe Day
forall a. Maybe a
Nothing ([Maybe Day] -> Maybe Day)
-> ([Maybe Day] -> [Maybe Day]) -> [Maybe Day] -> Maybe Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Day -> Maybe Day -> Ordering) -> [Maybe Day] -> [Maybe Day]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy Maybe Day -> Maybe Day -> Ordering
compareMaybeDates ([Maybe Day] -> [Maybe Day])
-> ([Maybe Day] -> [Maybe Day]) -> [Maybe Day] -> [Maybe Day]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Day -> Bool) -> [Maybe Day] -> [Maybe Day]
forall a. (a -> Bool) -> [a] -> [a]
filter Maybe Day -> Bool
forall a. Maybe a -> Bool
isJust

-- | What is the latest of these dates, ignoring Nothings ?
latestMaybeDate' :: [Maybe Day] -> Maybe Day
latestMaybeDate' :: [Maybe Day] -> Maybe Day
latestMaybeDate' = Maybe Day -> [Maybe Day] -> Maybe Day
forall a. a -> [a] -> a
headDef Maybe Day
forall a. Maybe a
Nothing ([Maybe Day] -> Maybe Day)
-> ([Maybe Day] -> [Maybe Day]) -> [Maybe Day] -> Maybe Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Day -> Maybe Day -> Ordering) -> [Maybe Day] -> [Maybe Day]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy ((Maybe Day -> Maybe Day -> Ordering)
-> Maybe Day -> Maybe Day -> Ordering
forall a b c. (a -> b -> c) -> b -> a -> c
flip Maybe Day -> Maybe Day -> Ordering
compareMaybeDates) ([Maybe Day] -> [Maybe Day])
-> ([Maybe Day] -> [Maybe Day]) -> [Maybe Day] -> [Maybe Day]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Day -> Bool) -> [Maybe Day] -> [Maybe Day]
forall a. (a -> Bool) -> [a] -> [a]
filter Maybe Day -> Bool
forall a. Maybe a -> Bool
isJust

-- | Compare two maybe dates, Nothing is earliest.
compareMaybeDates :: Maybe Day -> Maybe Day -> Ordering
compareMaybeDates :: Maybe Day -> Maybe Day -> Ordering
compareMaybeDates Nothing Nothing = Ordering
EQ
compareMaybeDates Nothing (Just _) = Ordering
LT
compareMaybeDates (Just _) Nothing = Ordering
GT
compareMaybeDates (Just a :: Day
a) (Just b :: Day
b) = Day -> Day -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Day
a Day
b

-- | The depth limit this query specifies, or a large number if none.
queryDepth :: Query -> Int
queryDepth :: Query -> Int
queryDepth q :: Query
q = case Query -> [Int]
queryDepth' Query
q of [] -> 99999
                                     ds :: [Int]
ds -> [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum [Int]
ds
  where
    queryDepth' :: Query -> [Int]
queryDepth' (Depth d :: Int
d) = [Int
d]
    queryDepth' (Or qs :: [Query]
qs) = (Query -> [Int]) -> [Query] -> [Int]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Query -> [Int]
queryDepth' [Query]
qs
    queryDepth' (And qs :: [Query]
qs) = (Query -> [Int]) -> [Query] -> [Int]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Query -> [Int]
queryDepth' [Query]
qs
    queryDepth' _ = []

-- | The account we are currently focussed on, if any, and whether subaccounts are included.
-- Just looks at the first query option.
inAccount :: [QueryOpt] -> Maybe (AccountName,Bool)
inAccount :: [QueryOpt] -> Maybe (Text, Bool)
inAccount [] = Maybe (Text, Bool)
forall a. Maybe a
Nothing
inAccount (QueryOptInAcctOnly a :: Text
a:_) = (Text, Bool) -> Maybe (Text, Bool)
forall a. a -> Maybe a
Just (Text
a,Bool
False)
inAccount (QueryOptInAcct a :: Text
a:_) = (Text, Bool) -> Maybe (Text, Bool)
forall a. a -> Maybe a
Just (Text
a,Bool
True)

-- | A query for the account(s) we are currently focussed on, if any.
-- Just looks at the first query option.
inAccountQuery :: [QueryOpt] -> Maybe Query
inAccountQuery :: [QueryOpt] -> Maybe Query
inAccountQuery [] = Maybe Query
forall a. Maybe a
Nothing
inAccountQuery (QueryOptInAcctOnly a :: Text
a : _) = Query -> Maybe Query
forall a. a -> Maybe a
Just (Query -> Maybe Query) -> Query -> Maybe Query
forall a b. (a -> b) -> a -> b
$ String -> Query
Acct (String -> Query) -> String -> Query
forall a b. (a -> b) -> a -> b
$ Text -> String
accountNameToAccountOnlyRegex Text
a
inAccountQuery (QueryOptInAcct a :: Text
a     : _) = Query -> Maybe Query
forall a. a -> Maybe a
Just (Query -> Maybe Query) -> Query -> Maybe Query
forall a b. (a -> b) -> a -> b
$ String -> Query
Acct (String -> Query) -> String -> Query
forall a b. (a -> b) -> a -> b
$ Text -> String
accountNameToAccountRegex Text
a

-- -- | Convert a query to its inverse.
-- negateQuery :: Query -> Query
-- negateQuery =  Not

-- matching

-- | Does the match expression match this account ?
-- A matching in: clause is also considered a match.
matchesAccount :: Query -> AccountName -> Bool
matchesAccount :: Query -> Text -> Bool
matchesAccount (Query
None) _ = Bool
False
matchesAccount (Not m :: Query
m) a :: Text
a = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Query -> Text -> Bool
matchesAccount Query
m Text
a
matchesAccount (Or ms :: [Query]
ms) a :: Text
a = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> Text -> Bool
`matchesAccount` Text
a) [Query]
ms
matchesAccount (And ms :: [Query]
ms) a :: Text
a = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Query -> Text -> Bool
`matchesAccount` Text
a) [Query]
ms
matchesAccount (Acct r :: String
r) a :: Text
a = String -> String -> Bool
regexMatchesCI String
r (Text -> String
T.unpack Text
a) -- XXX pack
matchesAccount (Depth d :: Int
d) a :: Text
a = Text -> Int
accountNameLevel Text
a Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
d
matchesAccount (Tag _ _) _ = Bool
False
matchesAccount _ _ = Bool
True

matchesMixedAmount :: Query -> MixedAmount -> Bool
matchesMixedAmount :: Query -> MixedAmount -> Bool
matchesMixedAmount q :: Query
q (Mixed []) = Query
q Query -> Amount -> Bool
`matchesAmount` Amount
nullamt
matchesMixedAmount q :: Query
q (Mixed as :: [Amount]
as) = (Amount -> Bool) -> [Amount] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query
q Query -> Amount -> Bool
`matchesAmount`) [Amount]
as

matchesCommodity :: Query -> CommoditySymbol -> Bool
matchesCommodity :: Query -> Text -> Bool
matchesCommodity (Sym r :: String
r) s :: Text
s = String -> String -> Bool
regexMatchesCI ("^" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
r String -> ShowS
forall a. [a] -> [a] -> [a]
++ "$") (Text -> String
T.unpack Text
s)
matchesCommodity _ _ = Bool
True

-- | Does the match expression match this (simple) amount ?
matchesAmount :: Query -> Amount -> Bool
matchesAmount :: Query -> Amount -> Bool
matchesAmount (Not q :: Query
q) a :: Amount
a = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Query
q Query -> Amount -> Bool
`matchesAmount` Amount
a
matchesAmount (Query
Any) _ = Bool
True
matchesAmount (Query
None) _ = Bool
False
matchesAmount (Or qs :: [Query]
qs) a :: Amount
a = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> Amount -> Bool
`matchesAmount` Amount
a) [Query]
qs
matchesAmount (And qs :: [Query]
qs) a :: Amount
a = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Query -> Amount -> Bool
`matchesAmount` Amount
a) [Query]
qs
--
matchesAmount (Amt ord :: OrdPlus
ord n :: Quantity
n) a :: Amount
a = OrdPlus -> Quantity -> Amount -> Bool
compareAmount OrdPlus
ord Quantity
n Amount
a
matchesAmount (Sym r :: String
r) a :: Amount
a = Query -> Text -> Bool
matchesCommodity (String -> Query
Sym String
r) (Amount -> Text
acommodity Amount
a)
--
matchesAmount _ _ = Bool
True

-- | Is this simple (single-amount) mixed amount's quantity less than, greater than, equal to, or unsignedly equal to this number ?
-- For multi-amount (multiple commodities, or just unsimplified) mixed amounts this is always true.

-- | Is this amount's quantity less than, greater than, equal to, or unsignedly equal to this number ?
compareAmount :: OrdPlus -> Quantity -> Amount -> Bool
compareAmount :: OrdPlus -> Quantity -> Amount -> Bool
compareAmount ord :: OrdPlus
ord q :: Quantity
q Amount{aquantity :: Amount -> Quantity
aquantity=Quantity
aq} = case OrdPlus
ord of Lt      -> Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
<  Quantity
q
                                                       LtEq    -> Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
<= Quantity
q
                                                       Gt      -> Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
>  Quantity
q
                                                       GtEq    -> Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
>= Quantity
q
                                                       Eq      -> Quantity
aq Quantity -> Quantity -> Bool
forall a. Eq a => a -> a -> Bool
== Quantity
q
                                                       AbsLt   -> Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
<  Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
q
                                                       AbsLtEq -> Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
<= Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
q
                                                       AbsGt   -> Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
>  Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
q
                                                       AbsGtEq -> Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
aq Quantity -> Quantity -> Bool
forall a. Ord a => a -> a -> Bool
>= Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
q
                                                       AbsEq   -> Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
aq Quantity -> Quantity -> Bool
forall a. Eq a => a -> a -> Bool
== Quantity -> Quantity
forall a. Num a => a -> a
abs Quantity
q

-- | Does the match expression match this posting ?
--
-- Note that for account match we try both original and effective account
matchesPosting :: Query -> Posting -> Bool
matchesPosting :: Query -> Posting -> Bool
matchesPosting (Not q :: Query
q) p :: Posting
p = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Query
q Query -> Posting -> Bool
`matchesPosting` Posting
p
matchesPosting (Query
Any) _ = Bool
True
matchesPosting (Query
None) _ = Bool
False
matchesPosting (Or qs :: [Query]
qs) p :: Posting
p = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> Posting -> Bool
`matchesPosting` Posting
p) [Query]
qs
matchesPosting (And qs :: [Query]
qs) p :: Posting
p = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Query -> Posting -> Bool
`matchesPosting` Posting
p) [Query]
qs
matchesPosting (Code r :: String
r) p :: Posting
p = String -> String -> Bool
regexMatchesCI String
r (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ String -> (Transaction -> String) -> Maybe Transaction -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe "" (Text -> String
T.unpack (Text -> String) -> (Transaction -> Text) -> Transaction -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
tcode) (Maybe Transaction -> String) -> Maybe Transaction -> String
forall a b. (a -> b) -> a -> b
$ Posting -> Maybe Transaction
ptransaction Posting
p
matchesPosting (Desc r :: String
r) p :: Posting
p = String -> String -> Bool
regexMatchesCI String
r (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ String -> (Transaction -> String) -> Maybe Transaction -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe "" (Text -> String
T.unpack (Text -> String) -> (Transaction -> Text) -> Transaction -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
tdescription) (Maybe Transaction -> String) -> Maybe Transaction -> String
forall a b. (a -> b) -> a -> b
$ Posting -> Maybe Transaction
ptransaction Posting
p
matchesPosting (Acct r :: String
r) p :: Posting
p = Posting -> Bool
matchesPosting Posting
p Bool -> Bool -> Bool
|| Posting -> Bool
matchesPosting (Posting -> Posting
originalPosting Posting
p)
    where matchesPosting :: Posting -> Bool
matchesPosting p :: Posting
p = String -> String -> Bool
regexMatchesCI String
r (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ Posting -> Text
paccount Posting
p -- XXX pack
matchesPosting (Date span :: DateSpan
span) p :: Posting
p = DateSpan
span DateSpan -> Day -> Bool
`spanContainsDate` Posting -> Day
postingDate Posting
p
matchesPosting (Date2 span :: DateSpan
span) p :: Posting
p = DateSpan
span DateSpan -> Day -> Bool
`spanContainsDate` Posting -> Day
postingDate2 Posting
p
matchesPosting (StatusQ s :: Status
s) p :: Posting
p = Posting -> Status
postingStatus Posting
p Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
s
matchesPosting (Real v :: Bool
v) p :: Posting
p = Bool
v Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Posting -> Bool
isReal Posting
p
matchesPosting q :: Query
q@(Depth _) Posting{paccount :: Posting -> Text
paccount=Text
a} = Query
q Query -> Text -> Bool
`matchesAccount` Text
a
matchesPosting q :: Query
q@(Amt _ _) Posting{pamount :: Posting -> MixedAmount
pamount=MixedAmount
amt} = Query
q Query -> MixedAmount -> Bool
`matchesMixedAmount` MixedAmount
amt
-- matchesPosting q@(Amt _ _) Posting{pamount=amt} = q `matchesMixedAmount` amt
-- matchesPosting (Empty v) Posting{pamount=a} = v == isZeroMixedAmount a
-- matchesPosting (Empty False) Posting{pamount=a} = True
-- matchesPosting (Empty True) Posting{pamount=a} = isZeroMixedAmount a
matchesPosting (Empty _) _ = Bool
True
matchesPosting (Sym r :: String
r) Posting{pamount :: Posting -> MixedAmount
pamount=Mixed as :: [Amount]
as} = (Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> Text -> Bool
matchesCommodity (String -> Query
Sym String
r)) ([Text] -> Bool) -> [Text] -> Bool
forall a b. (a -> b) -> a -> b
$ (Amount -> Text) -> [Amount] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Amount -> Text
acommodity [Amount]
as
matchesPosting (Tag n :: String
n v :: Maybe String
v) p :: Posting
p = case (String
n, Maybe String
v) of
  ("payee", Just v :: String
v) -> Bool -> (Transaction -> Bool) -> Maybe Transaction -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (String -> String -> Bool
regexMatchesCI String
v (String -> Bool) -> (Transaction -> String) -> Transaction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> String) -> (Transaction -> Text) -> Transaction -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
transactionPayee) (Maybe Transaction -> Bool) -> Maybe Transaction -> Bool
forall a b. (a -> b) -> a -> b
$ Posting -> Maybe Transaction
ptransaction Posting
p
  ("note", Just v :: String
v) -> Bool -> (Transaction -> Bool) -> Maybe Transaction -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (String -> String -> Bool
regexMatchesCI String
v (String -> Bool) -> (Transaction -> String) -> Transaction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> String) -> (Transaction -> Text) -> Transaction -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
transactionNote) (Maybe Transaction -> Bool) -> Maybe Transaction -> Bool
forall a b. (a -> b) -> a -> b
$ Posting -> Maybe Transaction
ptransaction Posting
p
  (n :: String
n, v :: Maybe String
v) -> String -> Maybe String -> [(Text, Text)] -> Bool
matchesTags String
n Maybe String
v ([(Text, Text)] -> Bool) -> [(Text, Text)] -> Bool
forall a b. (a -> b) -> a -> b
$ Posting -> [(Text, Text)]
postingAllTags Posting
p

-- | Does the match expression match this transaction ?
matchesTransaction :: Query -> Transaction -> Bool
matchesTransaction :: Query -> Transaction -> Bool
matchesTransaction (Not q :: Query
q) t :: Transaction
t = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Query
q Query -> Transaction -> Bool
`matchesTransaction` Transaction
t
matchesTransaction (Query
Any) _ = Bool
True
matchesTransaction (Query
None) _ = Bool
False
matchesTransaction (Or qs :: [Query]
qs) t :: Transaction
t = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> Transaction -> Bool
`matchesTransaction` Transaction
t) [Query]
qs
matchesTransaction (And qs :: [Query]
qs) t :: Transaction
t = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Query -> Transaction -> Bool
`matchesTransaction` Transaction
t) [Query]
qs
matchesTransaction (Code r :: String
r) t :: Transaction
t = String -> String -> Bool
regexMatchesCI String
r (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ Transaction -> Text
tcode Transaction
t
matchesTransaction (Desc r :: String
r) t :: Transaction
t = String -> String -> Bool
regexMatchesCI String
r (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ Transaction -> Text
tdescription Transaction
t
matchesTransaction q :: Query
q@(Acct _) t :: Transaction
t = (Posting -> Bool) -> [Posting] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query
q Query -> Posting -> Bool
`matchesPosting`) ([Posting] -> Bool) -> [Posting] -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
matchesTransaction (Date span :: DateSpan
span) t :: Transaction
t = DateSpan -> Day -> Bool
spanContainsDate DateSpan
span (Day -> Bool) -> Day -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction -> Day
tdate Transaction
t
matchesTransaction (Date2 span :: DateSpan
span) t :: Transaction
t = DateSpan -> Day -> Bool
spanContainsDate DateSpan
span (Day -> Bool) -> Day -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction -> Day
transactionDate2 Transaction
t
matchesTransaction (StatusQ s :: Status
s) t :: Transaction
t = Transaction -> Status
tstatus Transaction
t Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
s
matchesTransaction (Real v :: Bool
v) t :: Transaction
t = Bool
v Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Transaction -> Bool
hasRealPostings Transaction
t
matchesTransaction q :: Query
q@(Amt _ _) t :: Transaction
t = (Posting -> Bool) -> [Posting] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query
q Query -> Posting -> Bool
`matchesPosting`) ([Posting] -> Bool) -> [Posting] -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
matchesTransaction (Empty _) _ = Bool
True
matchesTransaction (Depth d :: Int
d) t :: Transaction
t = (Posting -> Bool) -> [Posting] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Int -> Query
Depth Int
d Query -> Posting -> Bool
`matchesPosting`) ([Posting] -> Bool) -> [Posting] -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
matchesTransaction q :: Query
q@(Sym _) t :: Transaction
t = (Posting -> Bool) -> [Posting] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query
q Query -> Posting -> Bool
`matchesPosting`) ([Posting] -> Bool) -> [Posting] -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
matchesTransaction (Tag n :: String
n v :: Maybe String
v) t :: Transaction
t = case (String
n, Maybe String
v) of
  ("payee", Just v :: String
v) -> String -> String -> Bool
regexMatchesCI String
v (String -> Bool) -> (Transaction -> String) -> Transaction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> String) -> (Transaction -> Text) -> Transaction -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
transactionPayee (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction
t
  ("note", Just v :: String
v) -> String -> String -> Bool
regexMatchesCI String
v (String -> Bool) -> (Transaction -> String) -> Transaction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack (Text -> String) -> (Transaction -> Text) -> Transaction -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
transactionNote (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction
t
  (n :: String
n, v :: Maybe String
v) -> String -> Maybe String -> [(Text, Text)] -> Bool
matchesTags String
n Maybe String
v ([(Text, Text)] -> Bool) -> [(Text, Text)] -> Bool
forall a b. (a -> b) -> a -> b
$ Transaction -> [(Text, Text)]
transactionAllTags Transaction
t

-- | Filter a list of tags by matching against their names and
-- optionally also their values.
matchesTags :: Regexp -> Maybe Regexp -> [Tag] -> Bool
matchesTags :: String -> Maybe String -> [(Text, Text)] -> Bool
matchesTags namepat :: String
namepat valuepat :: Maybe String
valuepat = Bool -> Bool
not (Bool -> Bool)
-> ([(Text, Text)] -> Bool) -> [(Text, Text)] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Text, Text)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([(Text, Text)] -> Bool)
-> ([(Text, Text)] -> [(Text, Text)]) -> [(Text, Text)] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Text, Text) -> Bool) -> [(Text, Text)] -> [(Text, Text)]
forall a. (a -> Bool) -> [a] -> [a]
filter (String -> Maybe String -> (Text, Text) -> Bool
match String
namepat Maybe String
valuepat)
  where
    match :: String -> Maybe String -> (Text, Text) -> Bool
match npat :: String
npat Nothing     (n :: Text
n,_) = String -> String -> Bool
regexMatchesCI String
npat (Text -> String
T.unpack Text
n) -- XXX
    match npat :: String
npat (Just vpat :: String
vpat) (n :: Text
n,v :: Text
v) = String -> String -> Bool
regexMatchesCI String
npat (Text -> String
T.unpack Text
n) Bool -> Bool -> Bool
&& String -> String -> Bool
regexMatchesCI String
vpat (Text -> String
T.unpack Text
v)

-- | Does the query match this market price ?
matchesPriceDirective :: Query -> PriceDirective -> Bool
matchesPriceDirective :: Query -> PriceDirective -> Bool
matchesPriceDirective (Query
None) _      = Bool
False
matchesPriceDirective (Not q :: Query
q) p :: PriceDirective
p     = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Query -> PriceDirective -> Bool
matchesPriceDirective Query
q PriceDirective
p
matchesPriceDirective (Or qs :: [Query]
qs) p :: PriceDirective
p     = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Query -> PriceDirective -> Bool
`matchesPriceDirective` PriceDirective
p) [Query]
qs
matchesPriceDirective (And qs :: [Query]
qs) p :: PriceDirective
p    = (Query -> Bool) -> [Query] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Query -> PriceDirective -> Bool
`matchesPriceDirective` PriceDirective
p) [Query]
qs
matchesPriceDirective q :: Query
q@(Amt _ _) p :: PriceDirective
p = Query -> Amount -> Bool
matchesAmount Query
q (PriceDirective -> Amount
pdamount PriceDirective
p)
matchesPriceDirective q :: Query
q@(Sym _) p :: PriceDirective
p   = Query -> Text -> Bool
matchesCommodity Query
q (PriceDirective -> Text
pdcommodity PriceDirective
p)
matchesPriceDirective (Date span :: DateSpan
span) p :: PriceDirective
p = DateSpan -> Day -> Bool
spanContainsDate DateSpan
span (PriceDirective -> Day
pddate PriceDirective
p)
matchesPriceDirective _ _           = Bool
True


-- tests

tests_Query :: TestTree
tests_Query = String -> [TestTree] -> TestTree
tests "Query" [
   String -> Assertion -> TestTree
test "simplifyQuery" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
Or [String -> Query
Acct "a"])      Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (String -> Query
Acct "a")
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
Or [Query
Any,Query
None])      Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query
Any)
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query
Any,Query
None])     Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query
None)
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query
Any,Query
Any])      Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query
Any)
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [String -> Query
Acct "b",Query
Any]) Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (String -> Query
Acct "b")
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query
Any,[Query] -> Query
And [DateSpan -> Query
Date (Maybe Day -> Maybe Day -> DateSpan
DateSpan Maybe Day
forall a. Maybe a
Nothing Maybe Day
forall a. Maybe a
Nothing)]]) Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query
Any)
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [DateSpan -> Query
Date (Maybe Day -> Maybe Day -> DateSpan
DateSpan Maybe Day
forall a. Maybe a
Nothing (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2013-01-01")), DateSpan -> Query
Date (Maybe Day -> Maybe Day -> DateSpan
DateSpan (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2012-01-01") Maybe Day
forall a. Maybe a
Nothing)])
       Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (DateSpan -> Query
Date (Maybe Day -> Maybe Day -> DateSpan
DateSpan (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2012-01-01") (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2013-01-01")))
     (Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [[Query] -> Query
Or [],[Query] -> Query
Or [String -> Query
Desc "b b"]]) Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (String -> Query
Desc "b b")

  ,String -> Assertion -> TestTree
test "parseQuery" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
     (Day -> Text -> (Query, [QueryOpt])
parseQuery Day
nulldate "acct:'expenses:autres d\233penses' desc:b") (Query, [QueryOpt]) -> (Query, [QueryOpt]) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ([Query] -> Query
And [String -> Query
Acct "expenses:autres d\233penses", String -> Query
Desc "b"], [])
     Day -> Text -> (Query, [QueryOpt])
parseQuery Day
nulldate "inacct:a desc:\"b b\""                     (Query, [QueryOpt]) -> (Query, [QueryOpt]) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (String -> Query
Desc "b b", [Text -> QueryOpt
QueryOptInAcct "a"])
     Day -> Text -> (Query, [QueryOpt])
parseQuery Day
nulldate "inacct:a inacct:b"                         (Query, [QueryOpt]) -> (Query, [QueryOpt]) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query
Any, [Text -> QueryOpt
QueryOptInAcct "a", Text -> QueryOpt
QueryOptInAcct "b"])
     Day -> Text -> (Query, [QueryOpt])
parseQuery Day
nulldate "desc:'x x'"                                (Query, [QueryOpt]) -> (Query, [QueryOpt]) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (String -> Query
Desc "x x", [])
     Day -> Text -> (Query, [QueryOpt])
parseQuery Day
nulldate "'a a' 'b"                                  (Query, [QueryOpt]) -> (Query, [QueryOpt]) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ([Query] -> Query
Or [String -> Query
Acct "a a",String -> Query
Acct "'b"], [])
     Day -> Text -> (Query, [QueryOpt])
parseQuery Day
nulldate "\""                                        (Query, [QueryOpt]) -> (Query, [QueryOpt]) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (String -> Query
Acct "\"", [])

  ,String -> Assertion -> TestTree
test "words''" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
      ([Text] -> Text -> [Text]
words'' [] "a b")                   [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["a","b"]
      ([Text] -> Text -> [Text]
words'' [] "'a b'")                 [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["a b"]
      ([Text] -> Text -> [Text]
words'' [] "not:a b")               [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["not:a","b"]
      ([Text] -> Text -> [Text]
words'' [] "not:'a b'")             [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["not:a b"]
      ([Text] -> Text -> [Text]
words'' [] "'not:a b'")             [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["not:a b"]
      ([Text] -> Text -> [Text]
words'' ["desc:"] "not:desc:'a b'") [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["not:desc:a b"]
      ([Text] -> Text -> [Text]
words'' [Text]
prefixes "\"acct:expenses:autres d\233penses\"") [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["acct:expenses:autres d\233penses"]
      ([Text] -> Text -> [Text]
words'' [Text]
prefixes "\"")              [Text] -> [Text] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ["\""]

  ,String -> Assertion -> TestTree
test "filterQuery" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
     (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth Query
Any       Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Query
Any
     (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth (Int -> Query
Depth 1) Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Int -> Query
Depth 1
     (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not(Bool -> Bool) -> (Query -> Bool) -> Query -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Query -> Bool
queryIsDepth) ([Query] -> Query
And [[Query] -> Query
And [Status -> Query
StatusQ Status
Cleared,Int -> Query
Depth 1]]) Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Status -> Query
StatusQ Status
Cleared
     (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth ([Query] -> Query
And [DateSpan -> Query
Date DateSpan
nulldatespan, Query -> Query
Not ([Query] -> Query
Or [Query
Any, Int -> Query
Depth 1])]) Query -> Query -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Query
Any   -- XXX unclear

  ,String -> Assertion -> TestTree
test "parseQueryTerm" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "a"                                Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Query
Acct "a")
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "acct:expenses:autres d\233penses" Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Query
Acct "expenses:autres d\233penses")
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "not:desc:a b"                     Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Query -> Query
Not (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ String -> Query
Desc "a b")
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "status:1"                         Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
Cleared)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "status:*"                         Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
Cleared)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "status:!"                         Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
Pending)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "status:0"                         Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
Unmarked)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "status:"                          Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
Unmarked)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "payee:x"                          Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> Query
Tag "payee" (String -> Maybe String
forall a. a -> Maybe a
Just "x"))
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "note:x"                           Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> Query
Tag "note" (String -> Maybe String
forall a. a -> Maybe a
Just "x"))
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "real:1"                           Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Bool -> Query
Real Bool
True)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "date:2008"                        Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
Date (DateSpan -> Query) -> DateSpan -> Query
forall a b. (a -> b) -> a -> b
$ Maybe Day -> Maybe Day -> DateSpan
DateSpan (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2008/01/01") (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2009/01/01"))
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "date:from 2012/5/17"              Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
Date (DateSpan -> Query) -> DateSpan -> Query
forall a b. (a -> b) -> a -> b
$ Maybe Day -> Maybe Day -> DateSpan
DateSpan (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2012/05/17") Maybe Day
forall a. Maybe a
Nothing)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "date:20180101-201804"             Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
Date (DateSpan -> Query) -> DateSpan -> Query
forall a b. (a -> b) -> a -> b
$ Maybe Day -> Maybe Day -> DateSpan
DateSpan (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2018/01/01") (Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ String -> Day
parsedate "2018/04/01"))
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "inacct:a"                         Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (QueryOpt -> Either Query QueryOpt
forall a b. b -> Either a b
Right (QueryOpt -> Either Query QueryOpt)
-> QueryOpt -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ Text -> QueryOpt
QueryOptInAcct "a")
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "tag:a"                            Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> Query
Tag "a" Maybe String
forall a. Maybe a
Nothing)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "tag:a=some value"                 Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> Query
Tag "a" (String -> Maybe String
forall a. a -> Maybe a
Just "some value"))
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "amt:<0"                           Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ OrdPlus -> Quantity -> Query
Amt OrdPlus
Lt 0)
     Day -> Text -> Either Query QueryOpt
parseQueryTerm Day
nulldate "amt:>10000.10"                    Either Query QueryOpt -> Either Query QueryOpt -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (Query -> Either Query QueryOpt
forall a b. a -> Either a b
Left (Query -> Either Query QueryOpt) -> Query -> Either Query QueryOpt
forall a b. (a -> b) -> a -> b
$ OrdPlus -> Quantity -> Query
Amt OrdPlus
AbsGt 10000.1)

  ,String -> Assertion -> TestTree
test "parseAmountQueryTerm" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
     Text -> (OrdPlus, Quantity)
parseAmountQueryTerm "<0"        (OrdPlus, Quantity) -> (OrdPlus, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (OrdPlus
Lt,0) -- special case for convenience, since AbsLt 0 would be always false
     Text -> (OrdPlus, Quantity)
parseAmountQueryTerm ">0"        (OrdPlus, Quantity) -> (OrdPlus, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (OrdPlus
Gt,0) -- special case for convenience and consistency with above
     Text -> (OrdPlus, Quantity)
parseAmountQueryTerm ">10000.10" (OrdPlus, Quantity) -> (OrdPlus, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (OrdPlus
AbsGt,10000.1)
     Text -> (OrdPlus, Quantity)
parseAmountQueryTerm "=0.23"     (OrdPlus, Quantity) -> (OrdPlus, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (OrdPlus
AbsEq,0.23)
     Text -> (OrdPlus, Quantity)
parseAmountQueryTerm "0.23"      (OrdPlus, Quantity) -> (OrdPlus, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (OrdPlus
AbsEq,0.23)
     Text -> (OrdPlus, Quantity)
parseAmountQueryTerm "<=+0.23"   (OrdPlus, Quantity) -> (OrdPlus, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (OrdPlus
LtEq,0.23)
     Text -> (OrdPlus, Quantity)
parseAmountQueryTerm "-0.23"     (OrdPlus, Quantity) -> (OrdPlus, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (OrdPlus
Eq,(-0.23))
    -- ,test "number beginning with decimal mark" $ parseAmountQueryTerm "=.23" @?= (AbsEq,0.23)  -- XXX

  ,String -> Assertion -> TestTree
test "matchesAccount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Query
Acct "b:c") Query -> Text -> Bool
`matchesAccount` "a:bb:c:d"
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Query
Acct "^a:b") Query -> Text -> Bool
`matchesAccount` "c:a:b"
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Int -> Query
Depth 2 Query -> Text -> Bool
`matchesAccount` "a"
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Int -> Query
Depth 2 Query -> Text -> Bool
`matchesAccount` "a:b"
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Int -> Query
Depth 2 Query -> Text -> Bool
`matchesAccount` "a:b:c"
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
Date DateSpan
nulldatespan Query -> Text -> Bool
`matchesAccount` "a"
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
Date2 DateSpan
nulldatespan Query -> Text -> Bool
`matchesAccount` "a"
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "a" Maybe String
forall a. Maybe a
Nothing) Query -> Text -> Bool
`matchesAccount` "a"

  ,String -> [TestTree] -> TestTree
tests "matchesPosting" [
     String -> Assertion -> TestTree
test "positive match on cleared posting status"  (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (Status -> Query
StatusQ Status
Cleared)  Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pstatus :: Status
pstatus=Status
Cleared}
    ,String -> Assertion -> TestTree
test "negative match on cleared posting status"  (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (Query -> Query
Not (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
Cleared)  Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pstatus :: Status
pstatus=Status
Cleared}
    ,String -> Assertion -> TestTree
test "positive match on unmarked posting status" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (Status -> Query
StatusQ Status
Unmarked) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pstatus :: Status
pstatus=Status
Unmarked}
    ,String -> Assertion -> TestTree
test "negative match on unmarked posting status" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (Query -> Query
Not (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ Status -> Query
StatusQ Status
Unmarked) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pstatus :: Status
pstatus=Status
Unmarked}
    ,String -> Assertion -> TestTree
test "positive match on true posting status acquired from transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (Status -> Query
StatusQ Status
Cleared) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pstatus :: Status
pstatus=Status
Unmarked,ptransaction :: Maybe Transaction
ptransaction=Transaction -> Maybe Transaction
forall a. a -> Maybe a
Just Transaction
nulltransaction{tstatus :: Status
tstatus=Status
Cleared}}
    ,String -> Assertion -> TestTree
test "real:1 on real posting" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (Bool -> Query
Real Bool
True) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptype :: PostingType
ptype=PostingType
RegularPosting}
    ,String -> Assertion -> TestTree
test "real:1 on virtual posting fails" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (Bool -> Query
Real Bool
True) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptype :: PostingType
ptype=PostingType
VirtualPosting}
    ,String -> Assertion -> TestTree
test "real:1 on balanced virtual posting fails" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (Bool -> Query
Real Bool
True) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptype :: PostingType
ptype=PostingType
BalancedVirtualPosting}
    ,String -> Assertion -> TestTree
test "acct:" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Query
Acct "'b") Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{paccount :: Text
paccount="'b"}
    ,String -> Assertion -> TestTree
test "tag:" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "a" (String -> Maybe String
forall a. a -> Maybe a
Just "r$")) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "foo" Maybe String
forall a. Maybe a
Nothing) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptags :: [(Text, Text)]
ptags=[("foo","")]}
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "foo" Maybe String
forall a. Maybe a
Nothing) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptags :: [(Text, Text)]
ptags=[("foo","baz")]}
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "foo" (String -> Maybe String
forall a. a -> Maybe a
Just "a")) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptags :: [(Text, Text)]
ptags=[("foo","bar")]}
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "foo" (String -> Maybe String
forall a. a -> Maybe a
Just "a$")) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptags :: [(Text, Text)]
ptags=[("foo","bar")]}
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag " foo " (String -> Maybe String
forall a. a -> Maybe a
Just "a")) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptags :: [(Text, Text)]
ptags=[("foo","bar")]}
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "foo foo" (String -> Maybe String
forall a. a -> Maybe a
Just " ar ba ")) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptags :: [(Text, Text)]
ptags=[("foo foo","bar bar")]}
    ,String -> Assertion -> TestTree
test "a tag match on a posting also sees inherited tags" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "txntag" Maybe String
forall a. Maybe a
Nothing) Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{ptransaction :: Maybe Transaction
ptransaction=Transaction -> Maybe Transaction
forall a. a -> Maybe a
Just Transaction
nulltransaction{ttags :: [(Text, Text)]
ttags=[("txntag","")]}}
    ,String -> Assertion -> TestTree
test "cur:" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Query
Sym "$") Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pamount :: MixedAmount
pamount=[Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1]} -- becomes "^$$", ie testing for null symbol
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Query
Sym "\\$") Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pamount :: MixedAmount
pamount=[Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1]} -- have to quote $ for regexpr
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Query
Sym "shekels") Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pamount :: MixedAmount
pamount=[Amount] -> MixedAmount
Mixed [Amount
nullamt{acommodity :: Text
acommodity="shekels"}]}
      HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Query
Sym "shek") Query -> Posting -> Bool
`matchesPosting` Posting
nullposting{pamount :: MixedAmount
pamount=[Amount] -> MixedAmount
Mixed [Amount
nullamt{acommodity :: Text
acommodity="shekels"}]}
  ]

  ,String -> Assertion -> TestTree
test "matchesTransaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Query
Any Query -> Transaction -> Bool
`matchesTransaction` Transaction
nulltransaction
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Query
Desc "x x") Query -> Transaction -> Bool
`matchesTransaction` Transaction
nulltransaction{tdescription :: Text
tdescription="x"}
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Query
Desc "x x") Query -> Transaction -> Bool
`matchesTransaction` Transaction
nulltransaction{tdescription :: Text
tdescription="x x"}
     -- see posting for more tag tests
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "foo" (String -> Maybe String
forall a. a -> Maybe a
Just "a")) Query -> Transaction -> Bool
`matchesTransaction` Transaction
nulltransaction{ttags :: [(Text, Text)]
ttags=[("foo","bar")]}
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "payee" (String -> Maybe String
forall a. a -> Maybe a
Just "payee")) Query -> Transaction -> Bool
`matchesTransaction` Transaction
nulltransaction{tdescription :: Text
tdescription="payee|note"}
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "note" (String -> Maybe String
forall a. a -> Maybe a
Just "note")) Query -> Transaction -> Bool
`matchesTransaction` Transaction
nulltransaction{tdescription :: Text
tdescription="payee|note"}
     -- a tag match on a transaction also matches posting tags
     HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ (String -> Maybe String -> Query
Tag "postingtag" Maybe String
forall a. Maybe a
Nothing) Query -> Transaction -> Bool
`matchesTransaction` Transaction
nulltransaction{tpostings :: [Posting]
tpostings=[Posting
nullposting{ptags :: [(Text, Text)]
ptags=[("postingtag","")]}]}

 ]