lib: make parseAmountQueryTerm total, improve docs/tests (#1312)
This commit is contained in:
		
							parent
							
								
									08ad220448
								
							
						
					
					
						commit
						38a4704641
					
				| @ -64,7 +64,7 @@ import Data.Monoid ((<>)) | |||||||
| #endif | #endif | ||||||
| import qualified Data.Text as T | import qualified Data.Text as T | ||||||
| import Data.Time.Calendar | import Data.Time.Calendar | ||||||
| import Safe (readDef, maximumByMay, maximumMay, minimumMay) | import Safe (readDef, readMay, maximumByMay, maximumMay, minimumMay) | ||||||
| import Text.Megaparsec | import Text.Megaparsec | ||||||
| import Text.Megaparsec.Char | import Text.Megaparsec.Char | ||||||
| 
 | 
 | ||||||
| @ -143,8 +143,11 @@ data QueryOpt = QueryOptInAcctOnly AccountName  -- ^ show an account register fo | |||||||
| -- showAccountMatcher _ = Nothing | -- showAccountMatcher _ = Nothing | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| -- | Convert a query expression containing zero or more space-separated | -- | Convert a query expression containing zero or more | ||||||
| -- terms to a query and zero or more query options. A query term is either: | -- space-separated terms to a query and zero or more query options; or | ||||||
|  | -- return an error message if query parsing fails. | ||||||
|  | -- | ||||||
|  | -- A query term is either: | ||||||
| -- | -- | ||||||
| -- 1. a search pattern, which matches on one or more fields, eg: | -- 1. a search pattern, which matches on one or more fields, eg: | ||||||
| -- | -- | ||||||
| @ -177,7 +180,6 @@ data QueryOpt = QueryOptInAcctOnly AccountName  -- ^ show an account register fo | |||||||
| -- (Or ([Acct "expenses:dining",Acct "out"]),[]) | -- (Or ([Acct "expenses:dining",Acct "out"]),[]) | ||||||
| -- >>> parseQuery nulldate "\"expenses:dining out\"" | -- >>> parseQuery nulldate "\"expenses:dining out\"" | ||||||
| -- (Acct "expenses:dining out",[]) | -- (Acct "expenses:dining out",[]) | ||||||
| -- |  | ||||||
| parseQuery :: Day -> T.Text -> (Query,[QueryOpt]) | parseQuery :: Day -> T.Text -> (Query,[QueryOpt]) | ||||||
| parseQuery d s = (q, opts) | parseQuery d s = (q, opts) | ||||||
|   where |   where | ||||||
| @ -273,7 +275,7 @@ parseQueryTerm _ (T.stripPrefix "status:" -> Just s) = | |||||||
|         case parseStatus s of Left e   -> error' $ "\"status:"++T.unpack s++"\" gave a parse error: " ++ e |         case parseStatus s of Left e   -> error' $ "\"status:"++T.unpack s++"\" gave a parse error: " ++ e | ||||||
|                               Right st -> Left $ StatusQ st |                               Right st -> Left $ StatusQ st | ||||||
| parseQueryTerm _ (T.stripPrefix "real:" -> Just s) = Left $ Real $ parseBool s || T.null s | parseQueryTerm _ (T.stripPrefix "real:" -> Just s) = Left $ Real $ parseBool s || T.null s | ||||||
| parseQueryTerm _ (T.stripPrefix "amt:" -> Just s) = Left $ Amt ord q where (ord, q) = parseAmountQueryTerm s | parseQueryTerm _ (T.stripPrefix "amt:" -> Just s) = Left $ Amt ord q where (ord, q) = either error id $ parseAmountQueryTerm s | ||||||
| parseQueryTerm _ (T.stripPrefix "empty:" -> Just s) = Left $ Empty $ parseBool s | parseQueryTerm _ (T.stripPrefix "empty:" -> Just s) = Left $ Empty $ parseBool s | ||||||
| parseQueryTerm _ (T.stripPrefix "depth:" -> Just s) | parseQueryTerm _ (T.stripPrefix "depth:" -> Just s) | ||||||
|   | n >= 0    = Left $ Depth n |   | n >= 0    = Left $ Depth n | ||||||
| @ -285,41 +287,49 @@ parseQueryTerm _ (T.stripPrefix "tag:" -> Just s) = Left $ Tag n v where (n,v) = | |||||||
| parseQueryTerm _ "" = Left $ Any | parseQueryTerm _ "" = Left $ Any | ||||||
| parseQueryTerm d s = parseQueryTerm d $ defaultprefix<>":"<>s | parseQueryTerm d s = parseQueryTerm d $ defaultprefix<>":"<>s | ||||||
| 
 | 
 | ||||||
| -- | Parse what comes after amt: . | -- | Parse the argument of an amt query term ([OP][SIGN]NUM), to an | ||||||
| parseAmountQueryTerm :: T.Text -> (OrdPlus, Quantity) | -- OrdPlus and a Quantity, or if parsing fails, an error message. OP | ||||||
| parseAmountQueryTerm s' = | -- can be <=, <, >=, >, or = . NUM can be a simple integer or decimal. | ||||||
|   case s' of | -- If a decimal, the decimal mark must be period, and it must have | ||||||
|     -- feel free to do this a smarter way | -- digits preceding it. Digit group marks are not allowed. | ||||||
|     ""              -> err | parseAmountQueryTerm :: T.Text -> Either String (OrdPlus, Quantity) | ||||||
|     (T.stripPrefix "<+" -> Just s)  -> (Lt, readDef err (T.unpack s)) | parseAmountQueryTerm amtarg = | ||||||
|     (T.stripPrefix "<=+" -> Just s) -> (LtEq, readDef err (T.unpack s)) |   case amtarg of | ||||||
|     (T.stripPrefix ">+" -> Just s)  -> (Gt, readDef err (T.unpack s)) |     -- number has a + sign, do a signed comparison | ||||||
|     (T.stripPrefix ">=+" -> Just s) -> (GtEq, readDef err (T.unpack s)) |     (parse "<=+" -> Just q) -> Right (LtEq    ,q) | ||||||
|     (T.stripPrefix "=+" -> Just s)  -> (Eq, readDef err (T.unpack s)) |     (parse "<+"  -> Just q) -> Right (Lt      ,q) | ||||||
|     (T.stripPrefix "+" -> Just s)   -> (Eq, readDef err (T.unpack s)) |     (parse ">=+" -> Just q) -> Right (GtEq    ,q) | ||||||
|     (T.stripPrefix "<-" -> Just s)  -> (Lt, negate $ readDef err (T.unpack s)) |     (parse ">+"  -> Just q) -> Right (Gt      ,q) | ||||||
|     (T.stripPrefix "<=-" -> Just s) -> (LtEq, negate $ readDef err (T.unpack s)) |     (parse "=+"  -> Just q) -> Right (Eq      ,q) | ||||||
|     (T.stripPrefix ">-" -> Just s)  -> (Gt, negate $ readDef err (T.unpack s)) |     (parse "+"   -> Just q) -> Right (Eq      ,q) | ||||||
|     (T.stripPrefix ">=-" -> Just s) -> (GtEq, negate $ readDef err (T.unpack s)) |     -- number has a - sign, do a signed comparison | ||||||
|     (T.stripPrefix "=-" -> Just s)  -> (Eq, negate $ readDef err (T.unpack s)) |     (parse "<-"  -> Just q) -> Right (Lt      ,-q) | ||||||
|     (T.stripPrefix "-" -> Just s)   -> (Eq, negate $ readDef err (T.unpack s)) |     (parse "<=-" -> Just q) -> Right (LtEq    ,-q) | ||||||
|     (T.stripPrefix "<=" -> Just s)  -> let n = readDef err (T.unpack s) in |     (parse ">-"  -> Just q) -> Right (Gt      ,-q) | ||||||
|                                          case n of |     (parse ">=-" -> Just q) -> Right (GtEq    ,-q) | ||||||
|                                            0 -> (LtEq, 0) |     (parse "=-"  -> Just q) -> Right (Eq      ,-q) | ||||||
|                                            _ -> (AbsLtEq, n) |     (parse "-"   -> Just q) -> Right (Eq      ,-q) | ||||||
|     (T.stripPrefix "<" -> Just s)   -> let n = readDef err (T.unpack s) in |     -- number is unsigned and zero, do a signed comparison (more useful) | ||||||
|                                          case n of 0 -> (Lt, 0) |     (parse "<="  -> Just 0) -> Right (LtEq    ,0) | ||||||
|                                                    _ -> (AbsLt, n) |     (parse "<"   -> Just 0) -> Right (Lt      ,0) | ||||||
|     (T.stripPrefix ">=" -> Just s)  -> let n = readDef err (T.unpack s) in |     (parse ">="  -> Just 0) -> Right (GtEq    ,0) | ||||||
|                                          case n of 0 -> (GtEq, 0) |     (parse ">"   -> Just 0) -> Right (Gt      ,0) | ||||||
|                                                    _ -> (AbsGtEq, n) |     -- number is unsigned and non-zero, do an absolute magnitude comparison | ||||||
|     (T.stripPrefix ">" -> Just s)   -> let n = readDef err (T.unpack s) in |     (parse "<="  -> Just q) -> Right (AbsLtEq ,q) | ||||||
|                                          case n of 0 -> (Gt, 0) |     (parse "<"   -> Just q) -> Right (AbsLt   ,q) | ||||||
|                                                    _ -> (AbsGt, n) |     (parse ">="  -> Just q) -> Right (AbsGtEq ,q) | ||||||
|     (T.stripPrefix "=" -> Just s)           -> (AbsEq, readDef err (T.unpack s)) |     (parse ">"   -> Just q) -> Right (AbsGt   ,q) | ||||||
|     s               -> (AbsEq, readDef err (T.unpack s)) |     (parse "="   -> Just q) -> Right (AbsEq   ,q) | ||||||
|  |     (parse ""    -> Just q) -> Right (AbsEq   ,q) | ||||||
|  |     _ -> Left $ | ||||||
|  |          "could not parse as a comparison operator followed by an optionally-signed number: " | ||||||
|  |          ++ T.unpack amtarg | ||||||
|   where |   where | ||||||
|     err = error' $ "could not parse as '=', '<', or '>' (optional) followed by a (optionally signed) numeric quantity: " ++ T.unpack s' |     -- Strip outer whitespace from the text, require and remove the | ||||||
|  |     -- specified prefix, remove all whitespace from the remainder, and | ||||||
|  |     -- read it as a simple integer or decimal if possible. | ||||||
|  |     parse :: T.Text -> T.Text -> Maybe Quantity | ||||||
|  |     parse p s = T.stripPrefix p s >>= readMay . T.unpack | ||||||
| 
 | 
 | ||||||
| parseTag :: T.Text -> (Regexp, Maybe Regexp) | parseTag :: T.Text -> (Regexp, Maybe Regexp) | ||||||
| parseTag s | "=" `T.isInfixOf` s = (T.unpack n, Just $ tail $ T.unpack v) | parseTag s | "=" `T.isInfixOf` s = (T.unpack n, Just $ tail $ T.unpack v) | ||||||
| @ -718,14 +728,15 @@ tests_Query = tests "Query" [ | |||||||
|      parseQueryTerm nulldate "amt:>10000.10"                    @?= (Left $ Amt AbsGt 10000.1) |      parseQueryTerm nulldate "amt:>10000.10"                    @?= (Left $ Amt AbsGt 10000.1) | ||||||
| 
 | 
 | ||||||
|   ,test "parseAmountQueryTerm" $ do |   ,test "parseAmountQueryTerm" $ do | ||||||
|      parseAmountQueryTerm "<0"        @?= (Lt,0) -- special case for convenience, since AbsLt 0 would be always false |      parseAmountQueryTerm "<0"        @?= Right (Lt,0) -- special case for convenience, since AbsLt 0 would be always false | ||||||
|      parseAmountQueryTerm ">0"        @?= (Gt,0) -- special case for convenience and consistency with above |      parseAmountQueryTerm ">0"        @?= Right (Gt,0) -- special case for convenience and consistency with above | ||||||
|      parseAmountQueryTerm ">10000.10" @?= (AbsGt,10000.1) |      parseAmountQueryTerm ">10000.10" @?= Right (AbsGt,10000.1) | ||||||
|      parseAmountQueryTerm "=0.23"     @?= (AbsEq,0.23) |      parseAmountQueryTerm "=0.23"     @?= Right (AbsEq,0.23) | ||||||
|      parseAmountQueryTerm "0.23"      @?= (AbsEq,0.23) |      parseAmountQueryTerm "0.23"      @?= Right (AbsEq,0.23) | ||||||
|      parseAmountQueryTerm "<=+0.23"   @?= (LtEq,0.23) |      parseAmountQueryTerm "<=+0.23"   @?= Right (LtEq,0.23) | ||||||
|      parseAmountQueryTerm "-0.23"     @?= (Eq,(-0.23)) |      parseAmountQueryTerm "-0.23"     @?= Right (Eq,(-0.23)) | ||||||
|     -- ,test "number beginning with decimal mark" $ parseAmountQueryTerm "=.23" @?= (AbsEq,0.23)  -- XXX |      assertLeft $ parseAmountQueryTerm "-0,23" | ||||||
|  |      assertLeft $ parseAmountQueryTerm "=.23" | ||||||
| 
 | 
 | ||||||
|   ,test "queryStartDate" $ do |   ,test "queryStartDate" $ do | ||||||
|      let small = Just $ fromGregorian 2000 01 01 |      let small = Just $ fromGregorian 2000 01 01 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user