From 8844c1e60a5ddee77d98e55e5b712dc9865755d7 Mon Sep 17 00:00:00 2001 From: Fabrice Reix Date: Sat, 19 Jun 2021 08:52:45 +0200 Subject: [PATCH] Add operators for arithmetic predicates --- integration/tests/assert_header.html | 2 +- integration/tests/assert_json.html | 2 +- integration/tests/assert_json.hurl | 4 + integration/tests/assert_json.json | 2 +- integration/tests/bytes.html | 2 +- integration/tests/cookies.html | 2 +- integration/tests/error_predicate.html | 2 +- .../tests/error_query_invalid_json.html | 2 +- .../tests/error_query_invalid_utf8.html | 2 +- packages/hurl/src/runner/predicate.rs | 16 +- packages/hurl_core/src/ast/core.rs | 138 ++++++++++--- packages/hurl_core/src/ast/display.rs | 68 +++++++ packages/hurl_core/src/format/html.rs | 168 +++++++++++----- packages/hurl_core/src/parser/predicate.rs | 189 ++++++++++++------ packages/hurl_core/src/parser/primitives.rs | 24 +++ packages/hurl_core/src/parser/sections.rs | 2 + packages/hurlfmt/src/format/json.rs | 1 + packages/hurlfmt/src/format/token.rs | 92 ++++----- packages/hurlfmt/src/linter/rules.rs | 15 ++ 19 files changed, 550 insertions(+), 183 deletions(-) diff --git a/integration/tests/assert_header.html b/integration/tests/assert_header.html index b66af8e7e..0b46f1644 100644 --- a/integration/tests/assert_header.html +++ b/integration/tests/assert_header.html @@ -1 +1 @@ -
GET http://localhost:8000/assert-header
HTTP/1.0 200Content-Type: text/html; charset=utf-8Set-Cookie: cookie1=value1; Path=/Set-Cookie: cookie2=value2; Path=/[Asserts]header "Custom" not existsheader "Content-Type" existsheader "Header1" equals "value1"header "ETag" equals ""33a64df551425fcc55e4d42a148795d9f25f89d4""header "Set-Cookie" existsheader "Set-Cookie" equals 3header "Set-Cookie" includes "cookie1=value1; Path=/"header "Set-Cookie" not includes "cookie4=value4; Path=/"
\ No newline at end of file +
GET http://localhost:8000/assert-header
HTTP/1.0 200Content-Type: text/html; charset=utf-8Set-Cookie: cookie1=value1; Path=/Set-Cookie: cookie2=value2; Path=/[Asserts]header "Custom" not existsheader "Content-Type" existsheader "Header1" equals "value1"header "ETag" equals ""33a64df551425fcc55e4d42a148795d9f25f89d4""header "Set-Cookie" existsheader "Set-Cookie" countEquals 3header "Set-Cookie" includes "cookie1=value1; Path=/"header "Set-Cookie" not includes "cookie4=value4; Path=/"
\ No newline at end of file diff --git a/integration/tests/assert_json.html b/integration/tests/assert_json.html index 892e4e1dc..cee81d6ea 100644 --- a/integration/tests/assert_json.html +++ b/integration/tests/assert_json.html @@ -1 +1 @@ -
GET http://localhost:8000/assert-json
HTTP/1.0 200[Asserts]jsonpath "$.count" equals 5jsonpath "$.count" equals 5.0jsonpath "$.count" greaterThan 1jsonpath "$.count" greaterThan 1.0jsonpath "$.success" equals falsejsonpath "$.success" not equals nulljsonpath "$.success" existsjsonpath "$.success" isBooleanjsonpath "$.errors" equals 2jsonpath "$.errors" isCollectionjsonpath "$.warnings" equals 0jsonpath "$.toto" not existsjsonpath "$.warnings" existsjsonpath "$.warnings" existsjsonpath "$.errors[0]" existsjsonpath "$.errors[0]" isCollectionjsonpath "$.errors[0].id" equals "error1"jsonpath "$.errors[0]['id']" equals "error1"jsonpath "$.errors[*].id" includes "error1"jsonpath "$.duration" equals 1.5jsonpath "$.duration" lessThanOrEquals 2.0jsonpath "$.duration" lessThan 2jsonpath "$.duration" isFloatjsonpath "$.duration" not isIntegerjsonpath "$.nullable" equals null{ "count": 5, "success": false, "errors": [{"id":"error1"},{"id":"error2"}], "warnings": [], "duration": 1.5, "tags": ["test"], "nullable": null}
GET http://localhost:8000/assert-json/index
HTTP/1.0 200[Captures]index: status
GET http://localhost:8000/assert-json
HTTP/1.0 200[Asserts]jsonpath "$.errors[{{index}}].id" equals "error2"jsonpath "$.tags" includes "test"jsonpath "$.tags" not includes "prod"jsonpath "$.tags" not includes null
GET http://localhost:8000/assert-json/list
HTTP/1.0 200[Asserts]jsonpath "$" equals 2jsonpath "$.[0].name" equals "Bob"jsonpath "$[0].name" equals "Bob"
\ No newline at end of file +
GET http://localhost:8000/assert-json
HTTP/1.0 200[Asserts]jsonpath "$.count" equals 5jsonpath "$.count" == 5jsonpath "$.count" equals 5.0jsonpath "$.count" greaterThan 1jsonpath "$.count" greaterThan 1.0jsonpath "$.count" >= 1.0jsonpath "$.success" equals falsejsonpath "$.success" not equals nulljsonpath "$.success" existsjsonpath "$.success" isBooleanjsonpath "$.errors" countEquals 2jsonpath "$.errors" isCollectionjsonpath "$.warnings" countEquals 0jsonpath "$.toto" not existsjsonpath "$.warnings" existsjsonpath "$.warnings" existsjsonpath "$.errors[0]" existsjsonpath "$.errors[0]" isCollectionjsonpath "$.errors[0].id" equals "error1"jsonpath "$.errors[0]['id']" equals "error1"jsonpath "$.errors[*].id" includes "error1"jsonpath "$.duration" equals 1.5jsonpath "$.duration" lessThanOrEquals 2.0jsonpath "$.duration" <= 2.0jsonpath "$.duration" lessThan 2jsonpath "$.duration" lessThan 2jsonpath "$.duration" isFloatjsonpath "$.duration" not isIntegerjsonpath "$.nullable" equals null{ "count": 5, "success": false, "errors": [{"id":"error1"},{"id":"error2"}], "warnings": [], "duration": 1.5, "tags": ["test"], "nullable": null}
GET http://localhost:8000/assert-json/index
HTTP/1.0 200[Captures]index: status
GET http://localhost:8000/assert-json
HTTP/1.0 200[Asserts]jsonpath "$.errors[{{index}}].id" equals "error2"jsonpath "$.tags" includes "test"jsonpath "$.tags" not includes "prod"jsonpath "$.tags" not includes null
GET http://localhost:8000/assert-json/list
HTTP/1.0 200[Asserts]jsonpath "$" countEquals 2jsonpath "$.[0].name" equals "Bob"jsonpath "$[0].name" equals "Bob"
\ No newline at end of file diff --git a/integration/tests/assert_json.hurl b/integration/tests/assert_json.hurl index acfb3023e..67b60790d 100644 --- a/integration/tests/assert_json.hurl +++ b/integration/tests/assert_json.hurl @@ -2,9 +2,11 @@ GET http://localhost:8000/assert-json HTTP/1.0 200 [Asserts] jsonpath "$.count" equals 5 +jsonpath "$.count" == 5 jsonpath "$.count" equals 5.0 jsonpath "$.count" greaterThan 1 jsonpath "$.count" greaterThan 1.0 +jsonpath "$.count" >= 1.0 jsonpath "$.success" equals false jsonpath "$.success" not equals null jsonpath "$.success" exists @@ -22,6 +24,8 @@ jsonpath "$.errors[0]['id']" equals "error1" jsonpath "$.errors[*].id" includes "error1" jsonpath "$.duration" equals 1.5 jsonpath "$.duration" lessThanOrEquals 2.0 +jsonpath "$.duration" <= 2.0 +jsonpath "$.duration" lessThan 2 jsonpath "$.duration" lessThan 2 jsonpath "$.duration" isFloat jsonpath "$.duration" not isInteger diff --git a/integration/tests/assert_json.json b/integration/tests/assert_json.json index c842efa78..6de9f6427 100644 --- a/integration/tests/assert_json.json +++ b/integration/tests/assert_json.json @@ -1 +1 @@ -{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5.0}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1.0}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"equal","value":false}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"not":true,"type":"equal","value":null}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"isBoolean"}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"count","value":0}},{"query":{"type":"jsonpath","expr":"$.toto"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.errors[0].id"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[0]['id']"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[*].id"},"predicate":{"type":"include","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"equal","value":1.5}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"less-or-equal","value":2.0}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"greater","value":2}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"isFloat"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"not":true,"type":"isInteger"}},{"query":{"type":"jsonpath","expr":"$.nullable"},"predicate":{"type":"equal","value":null}}],"body":{"type":"json","value":{"count":5,"success":false,"errors":[{"id":"error1"},{"id":"error2"}],"warnings":[],"duration":1.5,"tags":["test"],"nullable":null}}}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/index"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"index","query":{"type":"body"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.errors[{{index}}].id"},"predicate":{"type":"equal","value":"error2"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"type":"include","value":"test"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":"prod"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":null}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/list"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.[0].name"},"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"jsonpath","expr":"$[0].name"},"predicate":{"type":"equal","value":"Bob"}}]}}]} \ No newline at end of file +{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"equal","value":5.0}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater","value":1.0}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"greater-or-equal","value":1.0}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"equal","value":false}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"not":true,"type":"equal","value":null}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"isBoolean"}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"count","value":0}},{"query":{"type":"jsonpath","expr":"$.toto"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.errors[0].id"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[0]['id']"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[*].id"},"predicate":{"type":"include","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"equal","value":1.5}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"less-or-equal","value":2.0}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"less-or-equal","value":2.0}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"greater","value":2}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"greater","value":2}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"isFloat"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"not":true,"type":"isInteger"}},{"query":{"type":"jsonpath","expr":"$.nullable"},"predicate":{"type":"equal","value":null}}],"body":{"type":"json","value":{"count":5,"success":false,"errors":[{"id":"error1"},{"id":"error2"}],"warnings":[],"duration":1.5,"tags":["test"],"nullable":null}}}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/index"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"index","query":{"type":"body"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.errors[{{index}}].id"},"predicate":{"type":"equal","value":"error2"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"type":"include","value":"test"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":"prod"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":null}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/list"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.[0].name"},"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"jsonpath","expr":"$[0].name"},"predicate":{"type":"equal","value":"Bob"}}]}}]} \ No newline at end of file diff --git a/integration/tests/bytes.html b/integration/tests/bytes.html index 9348a1712..be543ccce 100644 --- a/integration/tests/bytes.html +++ b/integration/tests/bytes.html @@ -1 +1 @@ -
GET http://localhost:8000/bytes
HTTP/1.0 200Content-Type: application/octet-stream[Asserts]bytes equals hex,ff;bytes equals 1sha256 equals hex,a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89;
\ No newline at end of file +
GET http://localhost:8000/bytes
HTTP/1.0 200Content-Type: application/octet-stream[Asserts]bytes equals hex,ff;bytes countEquals 1sha256 equals hex,a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89;
\ No newline at end of file diff --git a/integration/tests/cookies.html b/integration/tests/cookies.html index 997e9053c..73ff00f00 100644 --- a/integration/tests/cookies.html +++ b/integration/tests/cookies.html @@ -1 +1 @@ -
# Request CookieGET http://localhost:8000/cookies/set-request-cookie1-valueA
[Cookies]cookie1: valueA
HTTP/1.0 200
# The cookie is not added in the cookie storageGET http://localhost:8000/cookies/assert-that-cookie1-is-not-in-session
HTTP/1.0 200
GET http://localhost:8000/cookies/set-multiple-request-cookies
[Cookies]user1: Bobuser2: Bill
HTTP/1.0 200
# Session CookieGET http://localhost:8000/cookies/set-session-cookie2-valueA
HTTP/1.0 200[Asserts]cookie "cookie2" equals "valueA"
GET http://localhost:8000/cookies/assert-that-cookie2-is-valueA
HTTP/1.0 200
GET http://localhost:8000/cookies/assert-that-cookie2-is-valueA-and-valueB
[Cookies]cookie2: valueB
HTTP/1.0 200
GET http://localhost:8000/cookies/delete-cookie2
HTTP/1.0 200[Asserts]cookie "cookie2" equals ""cookie "cookie2[Max-Age]" equals 0
GET http://localhost:8000/cookies/assert-that-cookie2-is-not-in-session
HTTP/1.0 200
GET http://localhost:8000/cookies/set
HTTP/1.0 200Set-Cookie: LSID=DQAAAKEaem_vYg; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/accountsSet-Cookie: HSID=AYQEVnDKrdst; Domain=.localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly; Path=/Set-Cookie: SSID=Ap4PGTEq; Domain=.localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/[Asserts]header "Set-Cookie" equals 3cookie "LSID" equals "DQAAAKEaem_vYg"cookie "LSID[Value]" equals "DQAAAKEaem_vYg"cookie "LSID[Expires]" existscookie "LSID[Expires]" equals "Wed, 13 Jan 2021 22:23:01 GMT"cookie "LSID[Max-Age]" not existscookie "LSID[Domain]" not existscookie "LSID[Path]" equals "/accounts"cookie "LSID[Secure]" existscookie "LSID[HttpOnly]" existscookie "LSID[SameSite]" not exists
+
# Request CookieGET http://localhost:8000/cookies/set-request-cookie1-valueA
[Cookies]cookie1: valueA
HTTP/1.0 200
# The cookie is not added in the cookie storageGET http://localhost:8000/cookies/assert-that-cookie1-is-not-in-session
HTTP/1.0 200
GET http://localhost:8000/cookies/set-multiple-request-cookies
[Cookies]user1: Bobuser2: Bill
HTTP/1.0 200
# Session CookieGET http://localhost:8000/cookies/set-session-cookie2-valueA
HTTP/1.0 200[Asserts]cookie "cookie2" equals "valueA"
GET http://localhost:8000/cookies/assert-that-cookie2-is-valueA
HTTP/1.0 200
GET http://localhost:8000/cookies/assert-that-cookie2-is-valueA-and-valueB
[Cookies]cookie2: valueB
HTTP/1.0 200
GET http://localhost:8000/cookies/delete-cookie2
HTTP/1.0 200[Asserts]cookie "cookie2" equals ""cookie "cookie2[Max-Age]" equals 0
GET http://localhost:8000/cookies/assert-that-cookie2-is-not-in-session
HTTP/1.0 200
GET http://localhost:8000/cookies/set
HTTP/1.0 200Set-Cookie: LSID=DQAAAKEaem_vYg; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/accountsSet-Cookie: HSID=AYQEVnDKrdst; Domain=.localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly; Path=/Set-Cookie: SSID=Ap4PGTEq; Domain=.localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/[Asserts]header "Set-Cookie" countEquals 3cookie "LSID" equals "DQAAAKEaem_vYg"cookie "LSID[Value]" equals "DQAAAKEaem_vYg"cookie "LSID[Expires]" existscookie "LSID[Expires]" equals "Wed, 13 Jan 2021 22:23:01 GMT"cookie "LSID[Max-Age]" not existscookie "LSID[Domain]" not existscookie "LSID[Path]" equals "/accounts"cookie "LSID[Secure]" existscookie "LSID[HttpOnly]" existscookie "LSID[SameSite]" not exists
\ No newline at end of file diff --git a/integration/tests/error_predicate.html b/integration/tests/error_predicate.html index ab51a5850..3e2b01c22 100644 --- a/integration/tests/error_predicate.html +++ b/integration/tests/error_predicate.html @@ -1 +1 @@ -
GET http://localhost:8000/predicate/error/type
HTTP/1.0 200[Asserts]jsonpath "$.status" equals "true"#jsonpath "$.count" equals "0"jsonpath "$.count" equals 0jsonpath "$.message" equals 0jsonpath "$.empty" equals 0jsonpath "$.number" equals 1.1jsonpath "$.message" startsWith "hi"jsonpath "$.message" contains "hi"jsonpath "$.message" matches "hi"jsonpath "$.message" equals 1jsonpath "$.toto" existsjsonpath "$.message" not existsjsonpath "$.list" equals 2
\ No newline at end of file +
GET http://localhost:8000/predicate/error/type
HTTP/1.0 200[Asserts]jsonpath "$.status" equals "true"#jsonpath "$.count" equals "0"jsonpath "$.count" equals 0jsonpath "$.message" equals 0jsonpath "$.empty" equals 0jsonpath "$.number" equals 1.1jsonpath "$.message" startsWith "hi"jsonpath "$.message" contains "hi"jsonpath "$.message" matches "hi"jsonpath "$.message" countEquals 1jsonpath "$.toto" existsjsonpath "$.message" not existsjsonpath "$.list" countEquals 2
\ No newline at end of file diff --git a/integration/tests/error_query_invalid_json.html b/integration/tests/error_query_invalid_json.html index 7ecac27ba..3da103fcb 100644 --- a/integration/tests/error_query_invalid_json.html +++ b/integration/tests/error_query_invalid_json.html @@ -1 +1 @@ -
GET http://localhost:8000/error-query-invalid-json
HTTP/1.0 200[Asserts]jsonpath "$.errors" equals 2
+
GET http://localhost:8000/error-query-invalid-json
HTTP/1.0 200[Asserts]jsonpath "$.errors" countEquals 2
\ No newline at end of file diff --git a/integration/tests/error_query_invalid_utf8.html b/integration/tests/error_query_invalid_utf8.html index b4dd8850c..f90389df4 100644 --- a/integration/tests/error_query_invalid_utf8.html +++ b/integration/tests/error_query_invalid_utf8.html @@ -1 +1 @@ -
GET http://localhost:8000/error-query-invalid-utf8
HTTP/1.0 200[Asserts]jsonpath "$.errors" equals 2
+
GET http://localhost:8000/error-query-invalid-utf8
HTTP/1.0 200[Asserts]jsonpath "$.errors" countEquals 2
\ No newline at end of file diff --git a/packages/hurl/src/runner/predicate.rs b/packages/hurl/src/runner/predicate.rs index 2ccff1cac..812b1df43 100644 --- a/packages/hurl/src/runner/predicate.rs +++ b/packages/hurl/src/runner/predicate.rs @@ -17,7 +17,6 @@ */ use std::collections::HashMap; -use hex; use regex::Regex; use hurl_core::ast::*; @@ -841,6 +840,7 @@ mod tests { value: PredicateFuncValue::EqualInt { space0: whitespace, value: 10, + operator: false, }, source_info: SourceInfo::init(1, 11, 1, 12), }, @@ -876,6 +876,7 @@ mod tests { value: PredicateFuncValue::EqualInt { space0: whitespace, value: 10, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -901,6 +902,7 @@ mod tests { value: PredicateFuncValue::EqualInt { space0: whitespace, value: 10, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -927,6 +929,7 @@ mod tests { value: PredicateFuncValue::EqualInt { space0: whitespace.clone(), value: 10, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -944,6 +947,7 @@ mod tests { value: PredicateFuncValue::EqualBool { space0: whitespace.clone(), value: true, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -965,6 +969,7 @@ mod tests { decimal: 200_000_000_000_000_000, decimal_digits: 0, }, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -1013,6 +1018,7 @@ mod tests { value: PredicateFuncValue::EqualInt { space0: whitespace.clone(), value: 1, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -1030,6 +1036,7 @@ mod tests { value: PredicateFuncValue::EqualBool { space0: whitespace.clone(), value: false, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -1051,6 +1058,7 @@ mod tests { decimal: 1, decimal_digits: 1, }, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -1069,6 +1077,7 @@ mod tests { value: PredicateFuncValue::EqualInt { space0: whitespace, value: 1, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -1114,6 +1123,7 @@ mod tests { value: PredicateFuncValue::EqualString { space0: whitespace.clone(), value: template.clone(), + operator: false, }, source_info: SourceInfo::init(1, 1, 1, 21), }, @@ -1139,6 +1149,7 @@ mod tests { value: PredicateFuncValue::EqualString { space0: whitespace, value: template, + operator: false, }, source_info: SourceInfo::init(0, 0, 0, 0), }, @@ -1422,6 +1433,7 @@ mod tests { source_info: SourceInfo::init(0, 0, 0, 0), value: PredicateFuncValue::EqualNull { space0: whitespace(), + operator: false, }, }, }; @@ -1471,6 +1483,7 @@ mod tests { source_info: SourceInfo::init(0, 0, 0, 0), value: PredicateFuncValue::EqualNull { space0: whitespace(), + operator: false, }, }, }; @@ -1493,6 +1506,7 @@ mod tests { source_info: SourceInfo::init(0, 0, 0, 0), value: PredicateFuncValue::EqualNull { space0: whitespace(), + operator: false, }, }, }; diff --git a/packages/hurl_core/src/ast/core.rs b/packages/hurl_core/src/ast/core.rs index d78e732fc..62651b0ec 100644 --- a/packages/hurl_core/src/ast/core.rs +++ b/packages/hurl_core/src/ast/core.rs @@ -400,31 +400,119 @@ pub struct PredicateFunc { #[derive(Clone, Debug, PartialEq, Eq)] #[allow(clippy::large_enum_variant)] pub enum PredicateFuncValue { - EqualString { space0: Whitespace, value: Template }, - EqualInt { space0: Whitespace, value: i64 }, - EqualFloat { space0: Whitespace, value: Float }, - EqualBool { space0: Whitespace, value: bool }, - EqualNull { space0: Whitespace }, - EqualHex { space0: Whitespace, value: Hex }, - EqualExpression { space0: Whitespace, value: Expr }, - GreaterThanInt { space0: Whitespace, value: i64 }, - GreaterThanFloat { space0: Whitespace, value: Float }, - GreaterThanOrEqualInt { space0: Whitespace, value: i64 }, - GreaterThanOrEqualFloat { space0: Whitespace, value: Float }, - LessThanInt { space0: Whitespace, value: i64 }, - LessThanFloat { space0: Whitespace, value: Float }, - LessThanOrEqualInt { space0: Whitespace, value: i64 }, - LessThanOrEqualFloat { space0: Whitespace, value: Float }, - CountEqual { space0: Whitespace, value: u64 }, - StartWith { space0: Whitespace, value: Template }, - Contain { space0: Whitespace, value: Template }, - IncludeString { space0: Whitespace, value: Template }, - IncludeInt { space0: Whitespace, value: i64 }, - IncludeFloat { space0: Whitespace, value: Float }, - IncludeBool { space0: Whitespace, value: bool }, - IncludeNull { space0: Whitespace }, - IncludeExpression { space0: Whitespace, value: Expr }, - Match { space0: Whitespace, value: Template }, + EqualString { + space0: Whitespace, + value: Template, + operator: bool, + }, + EqualInt { + space0: Whitespace, + value: i64, + operator: bool, + }, + EqualFloat { + space0: Whitespace, + value: Float, + operator: bool, + }, + EqualBool { + space0: Whitespace, + value: bool, + operator: bool, + }, + EqualNull { + space0: Whitespace, + operator: bool, + }, + EqualHex { + space0: Whitespace, + value: Hex, + operator: bool, + }, + EqualExpression { + space0: Whitespace, + value: Expr, + operator: bool, + }, + GreaterThanInt { + space0: Whitespace, + value: i64, + operator: bool, + }, + GreaterThanFloat { + space0: Whitespace, + value: Float, + operator: bool, + }, + GreaterThanOrEqualInt { + space0: Whitespace, + value: i64, + operator: bool, + }, + GreaterThanOrEqualFloat { + space0: Whitespace, + value: Float, + operator: bool, + }, + LessThanInt { + space0: Whitespace, + value: i64, + operator: bool, + }, + LessThanFloat { + space0: Whitespace, + value: Float, + operator: bool, + }, + LessThanOrEqualInt { + space0: Whitespace, + value: i64, + operator: bool, + }, + LessThanOrEqualFloat { + space0: Whitespace, + value: Float, + operator: bool, + }, + CountEqual { + space0: Whitespace, + value: u64, + }, + StartWith { + space0: Whitespace, + value: Template, + }, + Contain { + space0: Whitespace, + value: Template, + }, + IncludeString { + space0: Whitespace, + value: Template, + }, + IncludeInt { + space0: Whitespace, + value: i64, + }, + IncludeFloat { + space0: Whitespace, + value: Float, + }, + IncludeBool { + space0: Whitespace, + value: bool, + }, + IncludeNull { + space0: Whitespace, + }, + IncludeExpression { + space0: Whitespace, + value: Expr, + }, + Match { + space0: Whitespace, + value: Template, + }, IsInteger {}, IsFloat {}, IsBoolean {}, diff --git a/packages/hurl_core/src/ast/display.rs b/packages/hurl_core/src/ast/display.rs index 92b2b7f33..d578e8155 100644 --- a/packages/hurl_core/src/ast/display.rs +++ b/packages/hurl_core/src/ast/display.rs @@ -141,6 +141,74 @@ impl fmt::Display for Hex { } } +impl PredicateFuncValue { + pub fn name(&self) -> String { + match self { + PredicateFuncValue::EqualString { operator, .. } + | PredicateFuncValue::EqualInt { operator, .. } + | PredicateFuncValue::EqualFloat { operator, .. } + | PredicateFuncValue::EqualBool { operator, .. } + | PredicateFuncValue::EqualNull { operator, .. } + | PredicateFuncValue::EqualHex { operator, .. } + | PredicateFuncValue::EqualExpression { operator, .. } => { + if *operator { + "==".to_string() + } else { + "equals".to_string() + } + } + PredicateFuncValue::GreaterThanInt { operator, .. } + | PredicateFuncValue::GreaterThanFloat { operator, .. } => { + if *operator { + ">".to_string() + } else { + "greaterThan".to_string() + } + } + PredicateFuncValue::GreaterThanOrEqualInt { operator, .. } + | PredicateFuncValue::GreaterThanOrEqualFloat { operator, .. } => { + if *operator { + ">=".to_string() + } else { + "greaterThanOrEquals".to_string() + } + } + PredicateFuncValue::LessThanInt { operator, .. } + | PredicateFuncValue::LessThanFloat { operator, .. } => { + if *operator { + "<".to_string() + } else { + "lessThan".to_string() + } + } + PredicateFuncValue::LessThanOrEqualInt { operator, .. } + | PredicateFuncValue::LessThanOrEqualFloat { operator, .. } => { + if *operator { + "<=".to_string() + } else { + "lessThanOrEquals".to_string() + } + } + PredicateFuncValue::CountEqual { .. } => "countEquals".to_string(), + PredicateFuncValue::StartWith { .. } => "startsWith".to_string(), + PredicateFuncValue::Contain { .. } => "contains".to_string(), + PredicateFuncValue::IncludeString { .. } + | PredicateFuncValue::IncludeInt { .. } + | PredicateFuncValue::IncludeFloat { .. } + | PredicateFuncValue::IncludeBool { .. } + | PredicateFuncValue::IncludeNull { .. } + | PredicateFuncValue::IncludeExpression { .. } => "includes".to_string(), + PredicateFuncValue::Match { .. } => "matches".to_string(), + PredicateFuncValue::IsInteger { .. } => "isInteger".to_string(), + PredicateFuncValue::IsFloat { .. } => "isFloat".to_string(), + PredicateFuncValue::IsBoolean { .. } => "isBoolean".to_string(), + PredicateFuncValue::IsString { .. } => "isString".to_string(), + PredicateFuncValue::IsCollection { .. } => "isCollection".to_string(), + PredicateFuncValue::Exist { .. } => "exists".to_string(), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/hurl_core/src/format/html.rs b/packages/hurl_core/src/format/html.rs index 0becc3b7f..68687c791 100644 --- a/packages/hurl_core/src/format/html.rs +++ b/packages/hurl_core/src/format/html.rs @@ -445,181 +445,255 @@ impl Htmlable for PredicateFuncValue { fn to_html(&self) -> String { let mut buffer = String::from(""); match self { - PredicateFuncValue::CountEqual { space0, value } => { - buffer.push_str("equals"); + PredicateFuncValue::CountEqual { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str(format!("{}", value).as_str()); } - PredicateFuncValue::EqualString { space0, value } => { - buffer.push_str("equals"); + PredicateFuncValue::EqualString { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("\"{}\"", value.to_html()).as_str(), ); } - PredicateFuncValue::EqualInt { space0, value } => { - buffer.push_str("equals"); + PredicateFuncValue::EqualInt { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str(format!("{}", value).as_str()); } - PredicateFuncValue::EqualFloat { space0, value } => { - buffer.push_str("equals"); + PredicateFuncValue::EqualFloat { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::EqualHex { space0, value } => { - buffer.push_str("equals"); + PredicateFuncValue::EqualHex { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer .push_str(format!("{}", value.to_string()).as_str()); } - PredicateFuncValue::GreaterThanInt { space0, value } => { - buffer.push_str("greaterThan"); + PredicateFuncValue::GreaterThanInt { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::GreaterThanFloat { space0, value } => { - buffer.push_str("greaterThan"); + PredicateFuncValue::GreaterThanFloat { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::GreaterThanOrEqualInt { space0, value } => { - buffer.push_str("greaterThanOrEquals"); + PredicateFuncValue::GreaterThanOrEqualInt { + space0, + value, + operator, + } => { + buffer.push_str( + format!( + "{}", + if *operator { + ">=" + } else { + "greaterThanOrEquals" + } + ) + .as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::GreaterThanOrEqualFloat { space0, value } => { - buffer.push_str("greaterThanOrEquals"); + PredicateFuncValue::GreaterThanOrEqualFloat { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::LessThanInt { space0, value } => { - buffer.push_str("lessThan"); + PredicateFuncValue::LessThanInt { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::LessThanFloat { space0, value } => { - buffer.push_str("lessThan"); + PredicateFuncValue::LessThanFloat { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::LessThanOrEqualInt { space0, value } => { - buffer.push_str("lessThanOrEquals"); + PredicateFuncValue::LessThanOrEqualInt { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::LessThanOrEqualFloat { space0, value } => { - buffer.push_str("lessThanOrEquals"); + PredicateFuncValue::LessThanOrEqualFloat { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } - PredicateFuncValue::EqualExpression { space0, value } => { - buffer.push_str("equals"); + PredicateFuncValue::EqualExpression { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str(value.to_html().as_str()); } PredicateFuncValue::StartWith { space0, value } => { - buffer.push_str("startsWith"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("\"{}\"", value.to_html()).as_str(), ); } PredicateFuncValue::Contain { space0, value } => { - buffer.push_str("contains"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("\"{}\"", value.to_html()).as_str(), ); } PredicateFuncValue::IncludeString { space0, value } => { - buffer.push_str("includes"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("\"{}\"", value.to_html()).as_str(), ); } PredicateFuncValue::IncludeInt { space0, value } => { - buffer.push_str("includes"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str(format!("{}", value).as_str()); } PredicateFuncValue::IncludeNull { space0 } => { - buffer.push_str("includes"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str("null"); } PredicateFuncValue::IncludeBool { space0, value } => { - buffer.push_str("includes"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str(format!("{}", value).as_str()); } PredicateFuncValue::IncludeFloat { space0, value } => { - buffer.push_str("includes"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("{}", value.to_string()).as_str(), ); } PredicateFuncValue::IncludeExpression { space0, value } => { - buffer.push_str("includes"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push('"'); buffer.push_str(value.to_html().as_str()); buffer.push('"'); } PredicateFuncValue::Match { space0, value } => { - buffer.push_str("matches"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str( format!("\"{}\"", value.to_html()).as_str(), ); } - PredicateFuncValue::EqualNull { space0 } => { - buffer.push_str("equals"); + PredicateFuncValue::EqualNull { space0, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str("null"); } - PredicateFuncValue::EqualBool { space0, value } => { - buffer.push_str("equals"); + PredicateFuncValue::EqualBool { space0, value, .. } => { + buffer.push_str( + format!("{}", self.name()).as_str(), + ); buffer.push_str(space0.to_html().as_str()); buffer.push_str(format!("{}", value).as_str()); } PredicateFuncValue::IsInteger {} => { - buffer.push_str("isInteger"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); } PredicateFuncValue::IsFloat {} => { - buffer.push_str("isFloat"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); } PredicateFuncValue::IsBoolean {} => { - buffer.push_str("isBoolean"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); } PredicateFuncValue::IsString {} => { - buffer.push_str("isString"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); } PredicateFuncValue::IsCollection {} => { - buffer.push_str("isCollection"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); } PredicateFuncValue::Exist {} => { - buffer.push_str("exists"); + buffer.push_str( + format!("{}", self.name()).as_str(), + ); } } buffer diff --git a/packages/hurl_core/src/parser/predicate.rs b/packages/hurl_core/src/parser/predicate.rs index ce6c49157..dad34cb38 100644 --- a/packages/hurl_core/src/parser/predicate.rs +++ b/packages/hurl_core/src/parser/predicate.rs @@ -93,23 +93,48 @@ fn predicate_func_value(reader: &mut Reader) -> ParseResult<'static, PredicateFu } fn equal_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValue> { - try_literal("equals", reader)?; - let space0 = one_or_more_spaces(reader)?; + let operator = try_literals("equals", "==", reader)? == "=="; + let space0 = if operator { + zero_or_more_spaces(reader)? + } else { + one_or_more_spaces(reader)? + }; let start = reader.state.clone(); // TODO To be refactored - use idiomatic choice with correct recoverable behaviour + match predicate_value(reader) { - Ok(PredicateValue::Null {}) => Ok(PredicateFuncValue::EqualNull { space0 }), - Ok(PredicateValue::Bool { value }) => Ok(PredicateFuncValue::EqualBool { space0, value }), - Ok(PredicateValue::Int { value }) => Ok(PredicateFuncValue::EqualInt { space0, value }), - Ok(PredicateValue::Float { value }) => Ok(PredicateFuncValue::EqualFloat { space0, value }), - Ok(PredicateValue::Hex { value }) => Ok(PredicateFuncValue::EqualHex { space0, value }), - Ok(PredicateValue::Expression { value }) => { - Ok(PredicateFuncValue::EqualExpression { space0, value }) - } - Ok(PredicateValue::Template { value }) => { - Ok(PredicateFuncValue::EqualString { space0, value }) - } + Ok(PredicateValue::Null {}) => Ok(PredicateFuncValue::EqualNull { space0, operator }), + Ok(PredicateValue::Bool { value }) => Ok(PredicateFuncValue::EqualBool { + space0, + value, + operator, + }), + Ok(PredicateValue::Int { value }) => Ok(PredicateFuncValue::EqualInt { + space0, + value, + operator, + }), + Ok(PredicateValue::Float { value }) => Ok(PredicateFuncValue::EqualFloat { + space0, + value, + operator, + }), + Ok(PredicateValue::Hex { value }) => Ok(PredicateFuncValue::EqualHex { + space0, + value, + operator, + }), + Ok(PredicateValue::Expression { value }) => Ok(PredicateFuncValue::EqualExpression { + space0, + value, + operator, + }), + Ok(PredicateValue::Template { value }) => Ok(PredicateFuncValue::EqualString { + space0, + value, + operator, + }), Err(e) => match e.inner { ParseError::EscapeChar {} | ParseError::OddNumberOfHexDigits {} => Err(e), _ => Err(Error { @@ -122,17 +147,24 @@ fn equal_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncVal } fn greater_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValue> { - try_literal("greaterThan", reader)?; - let space0 = one_or_more_spaces(reader)?; + let operator = try_literals("greaterThan", ">=", reader)? == ">="; + let space0 = if operator { + zero_or_more_spaces(reader)? + } else { + one_or_more_spaces(reader)? + }; let start = reader.state.clone(); - match predicate_value(reader) { - Ok(PredicateValue::Int { value }) => { - Ok(PredicateFuncValue::GreaterThanInt { space0, value }) - } - Ok(PredicateValue::Float { value }) => { - Ok(PredicateFuncValue::GreaterThanFloat { space0, value }) - } + Ok(PredicateValue::Int { value }) => Ok(PredicateFuncValue::GreaterThanInt { + space0, + value, + operator, + }), + Ok(PredicateValue::Float { value }) => Ok(PredicateFuncValue::GreaterThanFloat { + space0, + value, + operator, + }), Ok(_) => Err(Error { pos: start.pos, recoverable: false, @@ -148,18 +180,26 @@ fn greater_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncV }, } } -fn greater_or_equal_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValue> { - try_literal("greaterThanOrEquals", reader)?; - let space0 = one_or_more_spaces(reader)?; - let start = reader.state.clone(); +fn greater_or_equal_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValue> { + let operator = try_literals("greaterThanOrEquals", ">=", reader)? == ">="; + let space0 = if operator { + zero_or_more_spaces(reader)? + } else { + one_or_more_spaces(reader)? + }; + let start = reader.state.clone(); match predicate_value(reader) { - Ok(PredicateValue::Int { value }) => { - Ok(PredicateFuncValue::GreaterThanOrEqualInt { space0, value }) - } - Ok(PredicateValue::Float { value }) => { - Ok(PredicateFuncValue::GreaterThanOrEqualFloat { space0, value }) - } + Ok(PredicateValue::Int { value }) => Ok(PredicateFuncValue::GreaterThanOrEqualInt { + space0, + value, + operator, + }), + Ok(PredicateValue::Float { value }) => Ok(PredicateFuncValue::GreaterThanOrEqualFloat { + space0, + value, + operator, + }), Ok(_) => Err(Error { pos: start.pos, recoverable: false, @@ -177,15 +217,24 @@ fn greater_or_equal_predicate(reader: &mut Reader) -> ParseResult<'static, Predi } fn less_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValue> { - try_literal("lessThan", reader)?; - let space0 = one_or_more_spaces(reader)?; + let operator = try_literals("lessThan", "<", reader)? == "<"; + let space0 = if operator { + zero_or_more_spaces(reader)? + } else { + one_or_more_spaces(reader)? + }; let start = reader.state.clone(); - match predicate_value(reader) { - Ok(PredicateValue::Int { value }) => Ok(PredicateFuncValue::LessThanInt { space0, value }), - Ok(PredicateValue::Float { value }) => { - Ok(PredicateFuncValue::LessThanFloat { space0, value }) - } + Ok(PredicateValue::Int { value }) => Ok(PredicateFuncValue::LessThanInt { + space0, + value, + operator, + }), + Ok(PredicateValue::Float { value }) => Ok(PredicateFuncValue::LessThanFloat { + space0, + value, + operator, + }), Ok(_) => Err(Error { pos: start.pos, recoverable: false, @@ -201,18 +250,26 @@ fn less_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValu }, } } -fn less_or_equal_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValue> { - try_literal("lessThanOrEquals", reader)?; - let space0 = one_or_more_spaces(reader)?; - let start = reader.state.clone(); +fn less_or_equal_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateFuncValue> { + let operator = try_literals("lessThanOrEquals", "<=", reader)? == "<="; + let space0 = if operator { + zero_or_more_spaces(reader)? + } else { + one_or_more_spaces(reader)? + }; + let start = reader.state.clone(); match predicate_value(reader) { - Ok(PredicateValue::Int { value }) => { - Ok(PredicateFuncValue::LessThanOrEqualInt { space0, value }) - } - Ok(PredicateValue::Float { value }) => { - Ok(PredicateFuncValue::LessThanOrEqualFloat { space0, value }) - } + Ok(PredicateValue::Int { value }) => Ok(PredicateFuncValue::LessThanOrEqualInt { + space0, + value, + operator, + }), + Ok(PredicateValue::Float { value }) => Ok(PredicateFuncValue::LessThanOrEqualFloat { + space0, + value, + operator, + }), Ok(_) => Err(Error { pos: start.pos, recoverable: false, @@ -239,7 +296,7 @@ fn count_equal_predicate(reader: &mut Reader) -> ParseResult<'static, PredicateF pos: save.pos, recoverable: false, inner: ParseError::PredicateValue {}, - }) + }); } Ok(value) => value, }; @@ -401,6 +458,7 @@ mod tests { source_info: SourceInfo::init(1, 11, 1, 12), }, value: true, + operator: false }, }, } @@ -415,7 +473,7 @@ mod tests { error.pos, Pos { line: 1, - column: 13 + column: 13, } ); assert_eq!(error.recoverable, false); @@ -442,6 +500,8 @@ mod tests { value: String::from(" "), source_info: SourceInfo::init(1, 7, 1, 9), }, + + operator: false } ); @@ -452,12 +512,13 @@ mod tests { value: Float { int: 1, decimal: 100_000_000_000_000_000, - decimal_digits: 1 + decimal_digits: 1, }, space0: Whitespace { value: String::from(" "), source_info: SourceInfo::init(1, 7, 1, 8), }, + operator: false } ); @@ -470,7 +531,21 @@ mod tests { value: String::from(" "), source_info: SourceInfo::init(1, 7, 1, 8), }, - } + operator: false + }, + ); + + let mut reader = Reader::init("== 2"); + assert_eq!( + equal_predicate(&mut reader).unwrap(), + PredicateFuncValue::EqualInt { + value: 2, + space0: Whitespace { + value: String::from(" "), + source_info: SourceInfo::init(1, 3, 1, 4), + }, + operator: true + }, ); let mut reader = Reader::init("equals \"Bob\""); @@ -489,6 +564,7 @@ mod tests { value: String::from(" "), source_info: SourceInfo::init(1, 7, 1, 8), }, + operator: false } ); } @@ -517,6 +593,7 @@ mod tests { value: String::from(" "), source_info: SourceInfo::init(1, 7, 1, 8), }, + operator: false } ); } @@ -541,7 +618,7 @@ mod tests { error.pos, Pos { line: 1, - column: 13 + column: 13, } ); assert_eq!(error.recoverable, false); @@ -556,7 +633,7 @@ mod tests { error.pos, Pos { line: 1, - column: 12 + column: 12, } ); assert_eq!(error.recoverable, false); @@ -589,7 +666,7 @@ mod tests { value: Float { int: 1, decimal: 100_000_000_000_000_000, - decimal_digits: 1 + decimal_digits: 1, } } ); diff --git a/packages/hurl_core/src/parser/primitives.rs b/packages/hurl_core/src/parser/primitives.rs index 893e8cfb1..25f98c839 100644 --- a/packages/hurl_core/src/parser/primitives.rs +++ b/packages/hurl_core/src/parser/primitives.rs @@ -210,6 +210,30 @@ pub fn try_literal(s: &str, reader: &mut Reader) -> ParseResult<'static, ()> { } } +// return the literal string +pub fn try_literals(s1: &str, s2: &str, reader: &mut Reader) -> ParseResult<'static, String> { + let start = reader.state.clone(); + match literal(s1, reader) { + Ok(_) => Ok(s1.to_string()), + Err(_) => { + reader.state = start.clone(); + match literal(s2, reader) { + Ok(_) => Ok(s2.to_string()), + Err(_) => { + reader.state = start.clone(); + return Err(Error { + pos: start.pos, + recoverable: true, + inner: ParseError::Expecting { + value: format!("<{}> or <{}>", s1, s2), + }, + }); + } + } + } + } +} + pub fn newline(reader: &mut Reader) -> ParseResult<'static, Whitespace> { let start = reader.state.clone(); match try_literal("\r\n", reader) { diff --git a/packages/hurl_core/src/parser/sections.rs b/packages/hurl_core/src/parser/sections.rs index 25064044f..e05e532ae 100644 --- a/packages/hurl_core/src/parser/sections.rs +++ b/packages/hurl_core/src/parser/sections.rs @@ -445,6 +445,7 @@ mod tests { }], source_info: SourceInfo::init(2, 26, 2, 45), }, + operator: false }, }, }, @@ -782,6 +783,7 @@ mod tests { source_info: SourceInfo::init(1, 27, 1, 28), }, value: 5, + operator: false }, }, } diff --git a/packages/hurlfmt/src/format/json.rs b/packages/hurlfmt/src/format/json.rs index 5e6e4c5eb..e94f06a65 100644 --- a/packages/hurlfmt/src/format/json.rs +++ b/packages/hurlfmt/src/format/json.rs @@ -691,6 +691,7 @@ pub mod tests { value: PredicateFuncValue::EqualInt { space0: whitespace(), value, + operator: false, }, }, } diff --git a/packages/hurlfmt/src/format/token.rs b/packages/hurlfmt/src/format/token.rs index a60658f58..e7b976bab 100644 --- a/packages/hurlfmt/src/format/token.rs +++ b/packages/hurlfmt/src/format/token.rs @@ -528,149 +528,149 @@ impl Tokenizable for PredicateFuncValue { fn tokenize(&self) -> Vec { let mut tokens: Vec = vec![]; match self { - PredicateFuncValue::EqualNull { space0 } => { - tokens.push(Token::PredicateType(String::from("equals"))); + PredicateFuncValue::EqualNull { space0, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Keyword("null".to_string())); } - PredicateFuncValue::EqualBool { space0, value } => { - tokens.push(Token::PredicateType(String::from("equals"))); + PredicateFuncValue::EqualBool { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Boolean(value.to_string())); } - PredicateFuncValue::EqualString { space0, value } => { - tokens.push(Token::PredicateType(String::from("equals"))); + PredicateFuncValue::EqualString { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); add_tokens(&mut tokens, value.tokenize()); } - PredicateFuncValue::EqualInt { space0, value } => { - tokens.push(Token::PredicateType(String::from("equals"))); + PredicateFuncValue::EqualInt { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::EqualFloat { space0, value } => { - tokens.push(Token::PredicateType(String::from("equals"))); + PredicateFuncValue::EqualFloat { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::EqualHex { space0, value } => { - tokens.push(Token::PredicateType(String::from("equals"))); + PredicateFuncValue::EqualHex { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::String(value.to_string())); } - PredicateFuncValue::EqualExpression { space0, value } => { - tokens.push(Token::PredicateType(String::from("equals"))); + PredicateFuncValue::EqualExpression { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.append(&mut value.tokenize()); } - PredicateFuncValue::GreaterThanInt { space0, value } => { - tokens.push(Token::PredicateType(String::from("greaterThan"))); + PredicateFuncValue::GreaterThanInt { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::GreaterThanFloat { space0, value } => { - tokens.push(Token::PredicateType(String::from("greaterThan"))); + PredicateFuncValue::GreaterThanFloat { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::GreaterThanOrEqualInt { space0, value } => { - tokens.push(Token::PredicateType(String::from("greaterThanOrEquals"))); + PredicateFuncValue::GreaterThanOrEqualInt { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::GreaterThanOrEqualFloat { space0, value } => { - tokens.push(Token::PredicateType(String::from("greaterThanOrEquals"))); + PredicateFuncValue::GreaterThanOrEqualFloat { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::LessThanInt { space0, value } => { - tokens.push(Token::PredicateType(String::from("lessThan"))); + PredicateFuncValue::LessThanInt { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::LessThanFloat { space0, value } => { - tokens.push(Token::PredicateType(String::from("lessThan"))); + PredicateFuncValue::LessThanFloat { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::LessThanOrEqualInt { space0, value } => { - tokens.push(Token::PredicateType(String::from("lessThanOrEquals"))); + PredicateFuncValue::LessThanOrEqualInt { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } - PredicateFuncValue::LessThanOrEqualFloat { space0, value } => { - tokens.push(Token::PredicateType(String::from("lessThanOrEquals"))); + PredicateFuncValue::LessThanOrEqualFloat { space0, value, .. } => { + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } PredicateFuncValue::CountEqual { space0, value } => { - tokens.push(Token::PredicateType(String::from("countEquals"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Boolean(value.to_string())); } PredicateFuncValue::StartWith { space0, value } => { - tokens.push(Token::PredicateType(String::from("startsWith"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); add_tokens(&mut tokens, value.tokenize()); } PredicateFuncValue::Contain { space0, value } => { - tokens.push(Token::PredicateType(String::from("contains"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); add_tokens(&mut tokens, value.tokenize()); } PredicateFuncValue::IncludeString { space0, value } => { - tokens.push(Token::PredicateType(String::from("includes"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); add_tokens(&mut tokens, value.tokenize()); } PredicateFuncValue::IncludeInt { space0, value } => { - tokens.push(Token::PredicateType(String::from("includes"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } PredicateFuncValue::IncludeFloat { space0, value } => { - tokens.push(Token::PredicateType(String::from("includes"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Number(value.to_string())); } PredicateFuncValue::IncludeNull { space0 } => { - tokens.push(Token::PredicateType(String::from("includes"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Keyword("null".to_string())); } PredicateFuncValue::IncludeBool { space0, value } => { - tokens.push(Token::PredicateType(String::from("includes"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.push(Token::Boolean(value.to_string())); } PredicateFuncValue::IncludeExpression { space0, value } => { - tokens.push(Token::PredicateType(String::from("includes"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); tokens.append(&mut value.tokenize()); } PredicateFuncValue::Match { space0, value } => { - tokens.push(Token::PredicateType(String::from("matches"))); + tokens.push(Token::PredicateType(self.name())); add_tokens(&mut tokens, space0.tokenize()); add_tokens(&mut tokens, value.tokenize()); } PredicateFuncValue::IsInteger {} => { - tokens.push(Token::PredicateType(String::from("isInteger"))); + tokens.push(Token::PredicateType(self.name())); } PredicateFuncValue::IsFloat {} => { - tokens.push(Token::PredicateType(String::from("isFloat"))); + tokens.push(Token::PredicateType(self.name())); } PredicateFuncValue::IsBoolean {} => { - tokens.push(Token::PredicateType(String::from("isBoolean"))); + tokens.push(Token::PredicateType(self.name())); } PredicateFuncValue::IsString {} => { - tokens.push(Token::PredicateType(String::from("isString"))); + tokens.push(Token::PredicateType(self.name())); } PredicateFuncValue::IsCollection {} => { - tokens.push(Token::PredicateType(String::from("isCollection"))); + tokens.push(Token::PredicateType(self.name())); } PredicateFuncValue::Exist {} => { - tokens.push(Token::PredicateType(String::from("exists"))); + tokens.push(Token::PredicateType(self.name())); } } tokens diff --git a/packages/hurlfmt/src/linter/rules.rs b/packages/hurlfmt/src/linter/rules.rs index d4e8acbbb..25f35e2b5 100644 --- a/packages/hurlfmt/src/linter/rules.rs +++ b/packages/hurlfmt/src/linter/rules.rs @@ -389,76 +389,91 @@ impl Lintable for PredicateFuncValue { PredicateFuncValue::EqualString { value, .. } => PredicateFuncValue::EqualString { space0: one_whitespace(), value: value.clone().lint(), + operator: true, }, PredicateFuncValue::EqualInt { value, .. } => PredicateFuncValue::EqualInt { space0: one_whitespace(), value: value.clone(), + operator: true, }, PredicateFuncValue::EqualBool { value, .. } => PredicateFuncValue::EqualBool { space0: one_whitespace(), value: value.clone(), + operator: true, }, PredicateFuncValue::EqualNull { .. } => PredicateFuncValue::EqualNull { space0: one_whitespace(), + operator: true, }, PredicateFuncValue::EqualFloat { value, .. } => PredicateFuncValue::EqualFloat { space0: one_whitespace(), value: value.clone(), + operator: true, }, PredicateFuncValue::EqualHex { value, .. } => PredicateFuncValue::EqualHex { space0: one_whitespace(), value: value.lint(), + operator: true, }, PredicateFuncValue::EqualExpression { value, .. } => { PredicateFuncValue::EqualExpression { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::GreaterThanInt { value, .. } => { PredicateFuncValue::GreaterThanInt { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::GreaterThanFloat { value, .. } => { PredicateFuncValue::GreaterThanFloat { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::GreaterThanOrEqualInt { value, .. } => { PredicateFuncValue::GreaterThanOrEqualInt { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::GreaterThanOrEqualFloat { value, .. } => { PredicateFuncValue::GreaterThanOrEqualFloat { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::LessThanInt { value, .. } => PredicateFuncValue::GreaterThanInt { space0: one_whitespace(), value: value.clone(), + operator: true, }, PredicateFuncValue::LessThanFloat { value, .. } => { PredicateFuncValue::GreaterThanFloat { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::LessThanOrEqualInt { value, .. } => { PredicateFuncValue::GreaterThanOrEqualInt { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::LessThanOrEqualFloat { value, .. } => { PredicateFuncValue::GreaterThanOrEqualFloat { space0: one_whitespace(), value: value.clone(), + operator: true, } } PredicateFuncValue::Contain { value, .. } => PredicateFuncValue::Contain {