diff --git a/README.md b/README.md
index b86b1071..b9c15467 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[![GitHub Release](https://img.shields.io/github/v/release/jbzoo/csv-blueprint?label=Latest)](https://github.com/jbzoo/csv-blueprint/releases) [![Total Downloads](https://poser.pugx.org/jbzoo/csv-blueprint/downloads)](https://packagist.org/packages/jbzoo/csv-blueprint/stats) [![Docker Pulls](https://img.shields.io/docker/pulls/jbzoo/csv-blueprint.svg)](https://hub.docker.com/r/jbzoo/csv-blueprint/tags) [![Docker Image Size](https://img.shields.io/docker/image-size/jbzoo/csv-blueprint)](https://hub.docker.com/r/jbzoo/csv-blueprint/tags)
-[![Static Badge](https://img.shields.io/badge/Rules-282-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml) [![Static Badge](https://img.shields.io/badge/Rules-71-green?label=Cell%20rules&labelColor=blue&color=gray)](src/Rules/Cell) [![Static Badge](https://img.shields.io/badge/Rules-206-green?label=Aggregate%20rules&labelColor=blue&color=gray)](src/Rules/Aggregate) [![Static Badge](https://img.shields.io/badge/Rules-5-green?label=Extra%20checks&labelColor=blue&color=gray)](#extra-checks) [![Static Badge](https://img.shields.io/badge/Rules-207-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml)
+[![Static Badge](https://img.shields.io/badge/Rules-292-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml) [![Static Badge](https://img.shields.io/badge/Rules-81-green?label=Cell%20rules&labelColor=blue&color=gray)](src/Rules/Cell) [![Static Badge](https://img.shields.io/badge/Rules-206-green?label=Aggregate%20rules&labelColor=blue&color=gray)](src/Rules/Aggregate) [![Static Badge](https://img.shields.io/badge/Rules-5-green?label=Extra%20checks&labelColor=blue&color=gray)](#extra-checks) [![Static Badge](https://img.shields.io/badge/Rules-199-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml)
## Introduction
@@ -193,10 +193,11 @@ columns:
word_count_max: 9 # x <= 9
# Contains rules
- contains: Hello # Example: "Hello World".
- contains_one: [ a, b ] # At least one of the string must be part of the CSV value.
- contains_all: [ a, b, c ] # All the strings must be part of a CSV value.
+ contains: World # Example: "Hello World!". The string must contain "World" in any place.
contains_none: [ a, b ] # All the strings must NOT be part of a CSV value.
+ contains_one: [ a, b ] # Only one of the strings must be part of the CSV value.
+ contains_any: [ a, b ] # At least one of the string must be part of the CSV value.
+ contains_all: [ a, b ] # All the strings must be part of a CSV value.
starts_with: "prefix " # Example: "prefix Hello World".
ends_with: " suffix" # Example: "Hello World suffix".
@@ -241,16 +242,25 @@ columns:
# Specific formats
is_bool: true # Allow only boolean values "true" and "false", case-insensitive.
- is_ip4: true # Only IPv4. Example: "127.0.0.1".
- is_url: true # Only URL format. Example: "https://example.com/page?query=string#anchor".
- is_email: true # Only email format. Example: "user@example.com".
- is_domain: true # Only domain name. Example: "example.com".
is_uuid: true # Validates whether the input is a valid UUID. It also supports validation of specific versions 1, 3, 4 and 5.
is_slug: true # Only slug format. Example: "my-slug-123". It can contain letters, numbers, and dashes.
is_currency_code: true # Validates an ISO 4217 currency code like GBP or EUR. Case-sensitive. See: https://en.wikipedia.org/wiki/ISO_4217.
is_base64: true # Validate if a string is Base64-encoded. Example: "cmVzcGVjdCE=".
is_angle: true # Check if the cell value is a valid angle (0.0 to 360.0).
+ # Internet
+ is_ip: true # Both: IPv4 or IPv6.
+ is_ip_v4: true # Only IPv4. Example: "127.0.0.1".
+ is_ip_v6: true # Only IPv6. Example: "2001:0db8:85a3:08d3:1319:8a2e:0370:7334".
+ is_ip_private: true # IPv4 has ranges: 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16. IPv6 has ranges starting with FD or FC.
+ is_ip_reserved: true # IPv4 has ranges: 0.0.0.0/8, 169.254.0.0/16, 127.0.0.0/8 and 240.0.0.0/4. IPv6 has ranges: ::1/128, ::/128, ::ffff:0:0/96 and fe80::/10.
+ ip_v4_range: [ '127.0.0.1-127.0.0.5', '127.0.0.0/21' ] # Check subnet mask or range for IPv4. Address must be in one of the ranges.
+ is_mac_address: true # The input is a valid MAC address. Example: 00:00:5e:00:53:01
+ is_domain: true # Only domain name. Example: "example.com".
+ is_public_domain_suffix: true # The input is a public ICANN domain suffix. Example: "com", "nom.br", "net" etc.
+ is_url: true # Only URL format. Example: "https://example.com/page?query=string#anchor".
+ is_email: true # Only email format. Example: "user@example.com".
+
# Validates if the given input is a valid JSON.
# This is possible if you escape all special characters correctly and use a special CSV format.
is_json: true # Example: {"foo":"bar"}.
@@ -259,19 +269,21 @@ columns:
is_latitude: true # Can be integer or float. Example: 50.123456.
is_longitude: true # Can be integer or float. Example: -89.123456.
is_geohash: true # Check if the value is a valid geohash. Example: "u4pruydqqvj".
- is_cardinal_direction: true # Valid cardinal direction. Available values: "N", "S", "E", "W", "NE", "SE", "NW", "SW", "none", ""
+ is_cardinal_direction: true # Valid cardinal direction. Available values: ["N", "S", "E", "W", "NE", "SE", "NW", "SW", "none", ""]
is_usa_market_name: true # Check if the value is a valid USA market name. Example: "New York, NY".
# Validates whether the input is a country code in ISO 3166-1 standard.
# Available options: "alpha-2" (Ex: "US"), "alpha-3" (Ex: "USA"), "numeric" (Ex: "840").
# The rule uses data from iso-codes: https://salsa.debian.org/iso-codes-team/iso-codes.
- country_code: alpha-2 # Country code in ISO 3166-1 standard. Examples: "US", "USA", "840".
+ is_country_code: alpha-2 # Country code in ISO 3166-1 standard. Examples: "US", "USA", "840"
# Validates whether the input is language code based on ISO 639.
# Available options: "alpha-2" (Ex: "en"), "alpha-3" (Ex: "eng").
# See: https://en.wikipedia.org/wiki/ISO_639.
- language_code: alpha-2 # Examples: "en", "eng".
+ is_language_code: alpha-2 # Examples: "en", "eng"
+ is_file_exists: true # Check if file exists on the filesystem (It's FS IO operation!).
+ is_dir_exists: true # Check if directory exists on the filesystem (It's FS IO operation!).
####################################################################################################################
# Data validation for the entire(!) column using different data aggregation methods.
@@ -282,8 +294,8 @@ columns:
is_unique: true # All values in the column are unique.
# Check if the column is sorted in a specific order.
- # - Direction: "asc", "desc".
- # - Method: "natural", "regular", "numeric", "string".
+ # - Direction: ["asc", "desc"].
+ # - Method: ["natural", "regular", "numeric", "string"].
# See: https://www.php.net/manual/en/function.sort.php
is_sorted: [ asc, natural ] # Expected ascending order, natural sorting.
@@ -295,7 +307,7 @@ columns:
first_num_less: 8.0 # x < 8.0
first_num_max: 9.0 # x <= 9.0
first: Expected # First value in the column. Will be compared as strings.
- first_not: 'Not Expected' # Not allowed as the first value in the column. Will be compared as strings.
+ first_not: Not expected # Not allowed as the first value in the column. Will be compared as strings.
# N-th value in the column.
# The rule expects exactly two arguments: the first is the line number (without header), the second is the expected value.
@@ -307,7 +319,7 @@ columns:
nth_num_less: [ 42, 8.0 ] # x < 8.0
nth_num_max: [ 42, 9.0 ] # x <= 9.0
nth: [ 2, Expected ] # Nth value in the column. Will be compared as strings.
- nth_not: [ 2, 'Not expected' ] # Not allowed as the N-th value in the column. Will be compared as strings.
+ nth_not: [ 2, Not expected ] # Not allowed as the N-th value in the column. Will be compared as strings.
# Last number in the column. Expected value is float or integer.
last_num_min: 1.0 # x >= 1.0
@@ -317,7 +329,7 @@ columns:
last_num_less: 8.0 # x < 8.0
last_num_max: 9.0 # x <= 9.0
last: Expected # Last value in the column. Will be compared as strings.
- last_not: 'Not Expected' # Not allowed as the last value in the column. Will be compared as strings.
+ last_not: Not expected # Not allowed as the last value in the column. Will be compared as strings.
# Sum of the numbers in the column. Example: [1, 2, 3] => 6.
sum_min: 1.0 # x >= 1.0
@@ -495,18 +507,18 @@ columns:
# Linear interpolation between closest ranks method - Second variant, C = 1 P-th percentile (0 <= P <= 100) of a list of N ordered values (sorted from least to greatest).
# Similar method used in NumPy and Excel.
# See: https://en.wikipedia.org/wiki/Percentile#Second_variant.2C_.7F.27.22.60UNIQ--postMath-00000043-QINU.60.22.27.7F
- # Example: `[ 95, 1.234 ]` The 95th percentile in the column must be "1.234" (float).
- percentile_min: [ 95, 1.0 ] # x >= 1.0
- percentile_greater: [ 95, 2.0 ] # x > 2.0
- percentile_not: [ 95, 5.0 ] # x != 5.0
- percentile: [ 95, 7.0 ] # x == 7.0
- percentile_less: [ 95, 8.0 ] # x < 8.0
- percentile_max: [ 95, 9.0 ] # x <= 9.0
+ # Example: `[ 95.5, 1.234 ]` The 95.5th percentile in the column must be "1.234" (float).
+ percentile_min: [ 95.0, 1.0 ] # x >= 1.0
+ percentile_greater: [ 95.0, 2.0 ] # x > 2.0
+ percentile_not: [ 95.0, 5.0 ] # x != 5.0
+ percentile: [ 95.0, 7.0 ] # x == 7.0
+ percentile_less: [ 95.0, 8.0 ] # x < 8.0
+ percentile_max: [ 95.0, 9.0 ] # x <= 9.0
# Quartiles. Three points that divide the data set into four equal groups, each group comprising a quarter of the data.
# See: https://en.wikipedia.org/wiki/Quartile
- # There are multiple methods for computing quartiles: "exclusive", "inclusive". Exclusive is ussually classic.
- # Available types: "0%", "Q1", "Q2", "Q3", "100%", "IQR" (aka Interquartile Range)
+ # There are multiple methods for computing quartiles: ["exclusive", "inclusive"]. Exclusive is ussually classic.
+ # Available types: ["0%", "Q1", "Q2", "Q3", "100%", "IQR"] ("IQR" is Interquartile Range)
# Example: `[ inclusive, 'Q3', 42.0 ]` - the Q3 inclusive quartile is 50.0
quartiles_min: [ 'exclusive', '0%', 1.0 ] # x >= 1.0
quartiles_greater: [ 'inclusive', 'Q1', 2.0 ] # x > 2.0
@@ -753,7 +765,7 @@ Options:
Feel free to use glob pattrens. Usage examples:
/full/path/file.yml, p/file.yml, p/*.yml, p/**/*.yml, p/**/name-*.json, **/*.php, etc. (multiple values allowed)
-r, --report=REPORT Report output format. Available options:
- text, table, github, gitlab, teamcity, junit [default: "table"]
+ ["text", "table", "github", "gitlab", "teamcity", "junit"] [default: "table"]
-Q, --quick[=QUICK] Immediately terminate the check at the first error found.
Of course it will speed up the check, but you will get only 1 message out of many.
If any error is detected, the utility will return a non-zero exit code.
diff --git a/schema-examples/full.json b/schema-examples/full.json
index 389666c4..2e74c900 100644
--- a/schema-examples/full.json
+++ b/schema-examples/full.json
@@ -20,87 +20,99 @@
"example" : "Some example",
"rules" : {
- "not_empty" : true,
- "exact_value" : "Some string",
- "allow_values" : ["y", "n", ""],
- "not_allow_values" : ["invalid"],
-
- "regex" : "\/^[\\d]{2}$\/",
-
- "length_min" : 1,
- "length_greater" : 2,
- "length_not" : 0,
- "length" : 7,
- "length_less" : 8,
- "length_max" : 9,
-
- "is_trimmed" : true,
- "is_lowercase" : true,
- "is_uppercase" : true,
- "is_capitalize" : true,
-
- "word_count_min" : 1,
- "word_count_greater" : 2,
- "word_count_not" : 0,
- "word_count" : 7,
- "word_count_less" : 8,
- "word_count_max" : 9,
-
- "contains" : "Hello",
- "contains_one" : ["a", "b"],
- "contains_all" : ["a", "b", "c"],
- "contains_none" : ["a", "b"],
- "starts_with" : "prefix ",
- "ends_with" : " suffix",
-
- "num_min" : 1,
- "num_greater" : 2,
- "num_not" : 5,
- "num" : 7,
- "num_less" : 8,
- "num_max" : 9,
- "is_int" : true,
- "is_float" : true,
-
- "precision_min" : 1,
- "precision_greater" : 2,
- "precision_not" : 0,
- "precision" : 7,
- "precision_less" : 8,
- "precision_max" : 9,
-
- "date_min" : "-100 years",
- "date_greater" : "-99 days",
- "date_not" : "2006-01-02 15:04:05 -0700 Europe\/Rome",
- "date" : "01 Jan 2000",
- "date_less" : "now",
- "date_max" : "+1 day",
- "date_format" : "Y-m-d",
- "is_date" : true,
- "is_timezone" : true,
- "is_timezone_offset" : true,
- "is_time" : true,
- "is_leap_year" : true,
-
- "is_bool" : true,
- "is_ip4" : true,
- "is_url" : true,
- "is_email" : true,
- "is_domain" : true,
- "is_uuid" : true,
- "is_slug" : true,
- "is_currency_code" : true,
- "is_base64" : true,
- "is_angle" : true,
- "is_json" : true,
- "is_latitude" : true,
- "is_longitude" : true,
- "is_geohash" : true,
- "is_cardinal_direction" : true,
- "is_usa_market_name" : true,
-
- "country_code" : "alpha-2",
- "language_code" : "alpha-2"
+ "not_empty" : true,
+ "exact_value" : "Some string",
+ "allow_values" : ["y", "n", ""],
+ "not_allow_values" : ["invalid"],
+
+ "regex" : "\/^[\\d]{2}$\/",
+
+ "length_min" : 1,
+ "length_greater" : 2,
+ "length_not" : 0,
+ "length" : 7,
+ "length_less" : 8,
+ "length_max" : 9,
+
+ "is_trimmed" : true,
+ "is_lowercase" : true,
+ "is_uppercase" : true,
+ "is_capitalize" : true,
+
+ "word_count_min" : 1,
+ "word_count_greater" : 2,
+ "word_count_not" : 0,
+ "word_count" : 7,
+ "word_count_less" : 8,
+ "word_count_max" : 9,
+
+ "contains" : "World",
+ "contains_none" : ["a", "b"],
+ "contains_one" : ["a", "b"],
+ "contains_any" : ["a", "b"],
+ "contains_all" : ["a", "b"],
+ "starts_with" : "prefix ",
+ "ends_with" : " suffix",
+
+ "num_min" : 1,
+ "num_greater" : 2,
+ "num_not" : 5,
+ "num" : 7,
+ "num_less" : 8,
+ "num_max" : 9,
+ "is_int" : true,
+ "is_float" : true,
+
+ "precision_min" : 1,
+ "precision_greater" : 2,
+ "precision_not" : 0,
+ "precision" : 7,
+ "precision_less" : 8,
+ "precision_max" : 9,
+
+ "date_min" : "-100 years",
+ "date_greater" : "-99 days",
+ "date_not" : "2006-01-02 15:04:05 -0700 Europe\/Rome",
+ "date" : "01 Jan 2000",
+ "date_less" : "now",
+ "date_max" : "+1 day",
+ "date_format" : "Y-m-d",
+ "is_date" : true,
+ "is_timezone" : true,
+ "is_timezone_offset" : true,
+ "is_time" : true,
+ "is_leap_year" : true,
+
+ "is_bool" : true,
+ "is_uuid" : true,
+ "is_slug" : true,
+ "is_currency_code" : true,
+ "is_base64" : true,
+ "is_angle" : true,
+
+ "is_ip" : true,
+ "is_ip_v4" : true,
+ "is_ip_v6" : true,
+ "is_ip_private" : true,
+ "is_ip_reserved" : true,
+ "ip_v4_range" : ["127.0.0.1-127.0.0.5", "127.0.0.0\/21"],
+ "is_mac_address" : true,
+ "is_domain" : true,
+ "is_public_domain_suffix" : true,
+ "is_url" : true,
+ "is_email" : true,
+
+ "is_json" : true,
+ "is_latitude" : true,
+ "is_longitude" : true,
+ "is_geohash" : true,
+ "is_cardinal_direction" : true,
+ "is_usa_market_name" : true,
+
+ "is_country_code" : "alpha-2",
+ "is_language_code" : "alpha-2",
+ "is_file_exists" : true,
+ "is_dir_exists" : true
},
"aggregate_rules" : {
"is_unique" : true,
@@ -113,7 +125,7 @@
"first_num_less" : 8,
"first_num_max" : 9,
"first" : "Expected",
- "first_not" : "Not Expected",
+ "first_not" : "Not expected",
"nth_num_min" : [42, 1],
"nth_num_greater" : [42, 2],
@@ -131,7 +143,7 @@
"last_num_less" : 8,
"last_num_max" : 9,
"last" : "Expected",
- "last_not" : "Not Expected",
+ "last_not" : "Not expected",
"sum_min" : 1,
"sum_greater" : 2,
diff --git a/schema-examples/full.php b/schema-examples/full.php
index 57123df4..b9965e34 100644
--- a/schema-examples/full.php
+++ b/schema-examples/full.php
@@ -67,10 +67,11 @@
'word_count_less' => 8,
'word_count_max' => 9,
- 'contains' => 'Hello',
- 'contains_one' => ['a', 'b'],
- 'contains_all' => ['a', 'b', 'c'],
+ 'contains' => 'World',
'contains_none' => ['a', 'b'],
+ 'contains_one' => ['a', 'b'],
+ 'contains_any' => ['a', 'b'],
+ 'contains_all' => ['a', 'b'],
'starts_with' => 'prefix ',
'ends_with' => ' suffix',
@@ -103,16 +104,25 @@
'is_time' => true,
'is_leap_year' => true,
- 'is_bool' => true,
- 'is_ip4' => true,
- 'is_url' => true,
- 'is_email' => true,
- 'is_domain' => true,
- 'is_uuid' => true,
- 'is_slug' => true,
- 'is_currency_code' => true,
- 'is_base64' => true,
- 'is_angle' => true,
+ 'is_bool' => true,
+ 'is_uuid' => true,
+ 'is_slug' => true,
+ 'is_currency_code' => true,
+ 'is_base64' => true,
+ 'is_angle' => true,
+
+ 'is_ip' => true,
+ 'is_ip_v4' => true,
+ 'is_ip_v6' => true,
+ 'is_ip_private' => true,
+ 'is_ip_reserved' => true,
+ 'ip_v4_range' => ['127.0.0.1-127.0.0.5', '127.0.0.0/21'],
+ 'is_mac_address' => true,
+ 'is_domain' => true,
+ 'is_public_domain_suffix' => true,
+ 'is_url' => true,
+ 'is_email' => true,
+
'is_json' => true,
'is_latitude' => true,
'is_longitude' => true,
@@ -120,8 +130,11 @@
'is_cardinal_direction' => true,
'is_usa_market_name' => true,
- 'country_code' => 'alpha-2',
- 'language_code' => 'alpha-2',
+ 'is_country_code' => 'alpha-2',
+ 'is_language_code' => 'alpha-2',
+
+ 'is_file_exists' => true,
+ 'is_dir_exists' => true,
],
'aggregate_rules' => [
@@ -135,7 +148,7 @@
'first_num_less' => 8.0,
'first_num_max' => 9.0,
'first' => 'Expected',
- 'first_not' => 'Not Expected',
+ 'first_not' => 'Not expected',
'nth_num_min' => [42, 1.0],
'nth_num_greater' => [42, 2.0],
@@ -153,7 +166,7 @@
'last_num_less' => 8.0,
'last_num_max' => 9.0,
'last' => 'Expected',
- 'last_not' => 'Not Expected',
+ 'last_not' => 'Not expected',
'sum_min' => 1.0,
'sum_greater' => 2.0,
@@ -295,12 +308,12 @@
'cubic_mean_less' => 8.0,
'cubic_mean_max' => 9.0,
- 'percentile_min' => [95, 1.0],
- 'percentile_greater' => [95, 2.0],
- 'percentile_not' => [95, 5.0],
- 'percentile' => [95, 7.0],
- 'percentile_less' => [95, 8.0],
- 'percentile_max' => [95, 9.0],
+ 'percentile_min' => [95.0, 1.0],
+ 'percentile_greater' => [95.0, 2.0],
+ 'percentile_not' => [95.0, 5.0],
+ 'percentile' => [95.0, 7.0],
+ 'percentile_less' => [95.0, 8.0],
+ 'percentile_max' => [95.0, 9.0],
'quartiles_min' => ['exclusive', '0%', 1.0],
'quartiles_greater' => ['inclusive', 'Q1', 2.0],
diff --git a/schema-examples/full.yml b/schema-examples/full.yml
index d605c0cb..4c00d56e 100644
--- a/schema-examples/full.yml
+++ b/schema-examples/full.yml
@@ -107,10 +107,11 @@ columns:
word_count_max: 9 # x <= 9
# Contains rules
- contains: Hello # Example: "Hello World".
- contains_one: [ a, b ] # At least one of the string must be part of the CSV value.
- contains_all: [ a, b, c ] # All the strings must be part of a CSV value.
+ contains: World # Example: "Hello World!". The string must contain "World" in any place.
contains_none: [ a, b ] # All the strings must NOT be part of a CSV value.
+ contains_one: [ a, b ] # Only one of the strings must be part of the CSV value.
+ contains_any: [ a, b ] # At least one of the string must be part of the CSV value.
+ contains_all: [ a, b ] # All the strings must be part of a CSV value.
starts_with: "prefix " # Example: "prefix Hello World".
ends_with: " suffix" # Example: "Hello World suffix".
@@ -155,16 +156,25 @@ columns:
# Specific formats
is_bool: true # Allow only boolean values "true" and "false", case-insensitive.
- is_ip4: true # Only IPv4. Example: "127.0.0.1".
- is_url: true # Only URL format. Example: "https://example.com/page?query=string#anchor".
- is_email: true # Only email format. Example: "user@example.com".
- is_domain: true # Only domain name. Example: "example.com".
is_uuid: true # Validates whether the input is a valid UUID. It also supports validation of specific versions 1, 3, 4 and 5.
is_slug: true # Only slug format. Example: "my-slug-123". It can contain letters, numbers, and dashes.
is_currency_code: true # Validates an ISO 4217 currency code like GBP or EUR. Case-sensitive. See: https://en.wikipedia.org/wiki/ISO_4217.
is_base64: true # Validate if a string is Base64-encoded. Example: "cmVzcGVjdCE=".
is_angle: true # Check if the cell value is a valid angle (0.0 to 360.0).
+ # Internet
+ is_ip: true # Both: IPv4 or IPv6.
+ is_ip_v4: true # Only IPv4. Example: "127.0.0.1".
+ is_ip_v6: true # Only IPv6. Example: "2001:0db8:85a3:08d3:1319:8a2e:0370:7334".
+ is_ip_private: true # IPv4 has ranges: 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16. IPv6 has ranges starting with FD or FC.
+ is_ip_reserved: true # IPv4 has ranges: 0.0.0.0/8, 169.254.0.0/16, 127.0.0.0/8 and 240.0.0.0/4. IPv6 has ranges: ::1/128, ::/128, ::ffff:0:0/96 and fe80::/10.
+ ip_v4_range: [ '127.0.0.1-127.0.0.5', '127.0.0.0/21' ] # Check subnet mask or range for IPv4. Address must be in one of the ranges.
+ is_mac_address: true # The input is a valid MAC address. Example: 00:00:5e:00:53:01
+ is_domain: true # Only domain name. Example: "example.com".
+ is_public_domain_suffix: true # The input is a public ICANN domain suffix. Example: "com", "nom.br", "net" etc.
+ is_url: true # Only URL format. Example: "https://example.com/page?query=string#anchor".
+ is_email: true # Only email format. Example: "user@example.com".
+
# Validates if the given input is a valid JSON.
# This is possible if you escape all special characters correctly and use a special CSV format.
is_json: true # Example: {"foo":"bar"}.
@@ -173,19 +183,21 @@ columns:
is_latitude: true # Can be integer or float. Example: 50.123456.
is_longitude: true # Can be integer or float. Example: -89.123456.
is_geohash: true # Check if the value is a valid geohash. Example: "u4pruydqqvj".
- is_cardinal_direction: true # Valid cardinal direction. Available values: "N", "S", "E", "W", "NE", "SE", "NW", "SW", "none", ""
+ is_cardinal_direction: true # Valid cardinal direction. Available values: ["N", "S", "E", "W", "NE", "SE", "NW", "SW", "none", ""]
is_usa_market_name: true # Check if the value is a valid USA market name. Example: "New York, NY".
# Validates whether the input is a country code in ISO 3166-1 standard.
# Available options: "alpha-2" (Ex: "US"), "alpha-3" (Ex: "USA"), "numeric" (Ex: "840").
# The rule uses data from iso-codes: https://salsa.debian.org/iso-codes-team/iso-codes.
- country_code: alpha-2 # Country code in ISO 3166-1 standard. Examples: "US", "USA", "840".
+ is_country_code: alpha-2 # Country code in ISO 3166-1 standard. Examples: "US", "USA", "840"
# Validates whether the input is language code based on ISO 639.
# Available options: "alpha-2" (Ex: "en"), "alpha-3" (Ex: "eng").
# See: https://en.wikipedia.org/wiki/ISO_639.
- language_code: alpha-2 # Examples: "en", "eng".
+ is_language_code: alpha-2 # Examples: "en", "eng"
+ is_file_exists: true # Check if file exists on the filesystem (It's FS IO operation!).
+ is_dir_exists: true # Check if directory exists on the filesystem (It's FS IO operation!).
####################################################################################################################
# Data validation for the entire(!) column using different data aggregation methods.
@@ -196,8 +208,8 @@ columns:
is_unique: true # All values in the column are unique.
# Check if the column is sorted in a specific order.
- # - Direction: "asc", "desc".
- # - Method: "natural", "regular", "numeric", "string".
+ # - Direction: ["asc", "desc"].
+ # - Method: ["natural", "regular", "numeric", "string"].
# See: https://www.php.net/manual/en/function.sort.php
is_sorted: [ asc, natural ] # Expected ascending order, natural sorting.
@@ -209,7 +221,7 @@ columns:
first_num_less: 8.0 # x < 8.0
first_num_max: 9.0 # x <= 9.0
first: Expected # First value in the column. Will be compared as strings.
- first_not: 'Not Expected' # Not allowed as the first value in the column. Will be compared as strings.
+ first_not: Not expected # Not allowed as the first value in the column. Will be compared as strings.
# N-th value in the column.
# The rule expects exactly two arguments: the first is the line number (without header), the second is the expected value.
@@ -221,7 +233,7 @@ columns:
nth_num_less: [ 42, 8.0 ] # x < 8.0
nth_num_max: [ 42, 9.0 ] # x <= 9.0
nth: [ 2, Expected ] # Nth value in the column. Will be compared as strings.
- nth_not: [ 2, 'Not expected' ] # Not allowed as the N-th value in the column. Will be compared as strings.
+ nth_not: [ 2, Not expected ] # Not allowed as the N-th value in the column. Will be compared as strings.
# Last number in the column. Expected value is float or integer.
last_num_min: 1.0 # x >= 1.0
@@ -231,7 +243,7 @@ columns:
last_num_less: 8.0 # x < 8.0
last_num_max: 9.0 # x <= 9.0
last: Expected # Last value in the column. Will be compared as strings.
- last_not: 'Not Expected' # Not allowed as the last value in the column. Will be compared as strings.
+ last_not: Not expected # Not allowed as the last value in the column. Will be compared as strings.
# Sum of the numbers in the column. Example: [1, 2, 3] => 6.
sum_min: 1.0 # x >= 1.0
@@ -409,18 +421,18 @@ columns:
# Linear interpolation between closest ranks method - Second variant, C = 1 P-th percentile (0 <= P <= 100) of a list of N ordered values (sorted from least to greatest).
# Similar method used in NumPy and Excel.
# See: https://en.wikipedia.org/wiki/Percentile#Second_variant.2C_.7F.27.22.60UNIQ--postMath-00000043-QINU.60.22.27.7F
- # Example: `[ 95, 1.234 ]` The 95th percentile in the column must be "1.234" (float).
- percentile_min: [ 95, 1.0 ] # x >= 1.0
- percentile_greater: [ 95, 2.0 ] # x > 2.0
- percentile_not: [ 95, 5.0 ] # x != 5.0
- percentile: [ 95, 7.0 ] # x == 7.0
- percentile_less: [ 95, 8.0 ] # x < 8.0
- percentile_max: [ 95, 9.0 ] # x <= 9.0
+ # Example: `[ 95.5, 1.234 ]` The 95.5th percentile in the column must be "1.234" (float).
+ percentile_min: [ 95.0, 1.0 ] # x >= 1.0
+ percentile_greater: [ 95.0, 2.0 ] # x > 2.0
+ percentile_not: [ 95.0, 5.0 ] # x != 5.0
+ percentile: [ 95.0, 7.0 ] # x == 7.0
+ percentile_less: [ 95.0, 8.0 ] # x < 8.0
+ percentile_max: [ 95.0, 9.0 ] # x <= 9.0
# Quartiles. Three points that divide the data set into four equal groups, each group comprising a quarter of the data.
# See: https://en.wikipedia.org/wiki/Quartile
- # There are multiple methods for computing quartiles: "exclusive", "inclusive". Exclusive is ussually classic.
- # Available types: "0%", "Q1", "Q2", "Q3", "100%", "IQR" (aka Interquartile Range)
+ # There are multiple methods for computing quartiles: ["exclusive", "inclusive"]. Exclusive is ussually classic.
+ # Available types: ["0%", "Q1", "Q2", "Q3", "100%", "IQR"] ("IQR" is Interquartile Range)
# Example: `[ inclusive, 'Q3', 42.0 ]` - the Q3 inclusive quartile is 50.0
quartiles_min: [ 'exclusive', '0%', 1.0 ] # x >= 1.0
quartiles_greater: [ 'inclusive', 'Q1', 2.0 ] # x > 2.0
diff --git a/schema-examples/full_clean.yml b/schema-examples/full_clean.yml
index 832cf8d3..042f1389 100644
--- a/schema-examples/full_clean.yml
+++ b/schema-examples/full_clean.yml
@@ -67,15 +67,17 @@ columns:
word_count_less: 8
word_count_max: 9
- contains: Hello
+ contains: World
+ contains_none:
+ - a
+ - b
contains_one:
- a
- b
- contains_all:
+ contains_any:
- a
- b
- - c
- contains_none:
+ contains_all:
- a
- b
starts_with: 'prefix '
@@ -111,23 +113,37 @@ columns:
is_leap_year: true
is_bool: true
- is_ip4: true
- is_url: true
- is_email: true
- is_domain: true
is_uuid: true
is_slug: true
is_currency_code: true
is_base64: true
is_angle: true
+
+ is_ip: true
+ is_ip_v4: true
+ is_ip_v6: true
+ is_ip_private: true
+ is_ip_reserved: true
+ ip_v4_range:
+ - 127.0.0.1-127.0.0.5
+ - 127.0.0.0/21
+ is_mac_address: true
+ is_domain: true
+ is_public_domain_suffix: true
+ is_url: true
+ is_email: true
+
is_json: true
is_latitude: true
is_longitude: true
is_geohash: true
is_cardinal_direction: true
is_usa_market_name: true
- country_code: alpha-2
- language_code: alpha-2
+ is_country_code: alpha-2
+ is_language_code: alpha-2
+
+ is_file_exists: true
+ is_dir_exists: true
aggregate_rules:
is_unique: true
@@ -142,7 +158,7 @@ columns:
first_num_less: 8.0
first_num_max: 9.0
first: Expected
- first_not: 'Not Expected'
+ first_not: Not expected
nth_num_min:
- 42
@@ -167,7 +183,7 @@ columns:
- Expected
nth_not:
- 2
- - 'Not expected'
+ - Not expected
last_num_min: 1.0
last_num_greater: 2.0
@@ -176,7 +192,7 @@ columns:
last_num_less: 8.0
last_num_max: 9.0
last: Expected
- last_not: 'Not Expected'
+ last_not: Not expected
sum_min: 1.0
sum_greater: 2.0
@@ -319,22 +335,22 @@ columns:
cubic_mean_max: 9.0
percentile_min:
- - 95
+ - 95.0
- 1.0
percentile_greater:
- - 95
+ - 95.0
- 2.0
percentile_not:
- - 95
+ - 95.0
- 5.0
percentile:
- - 95
+ - 95.0
- 7.0
percentile_less:
- - 95
+ - 95.0
- 8.0
percentile_max:
- - 95
+ - 95.0
- 9.0
quartiles_min:
diff --git a/src/Commands/ValidateCsv.php b/src/Commands/ValidateCsv.php
index 95f6c2c3..dca115a4 100644
--- a/src/Commands/ValidateCsv.php
+++ b/src/Commands/ValidateCsv.php
@@ -79,7 +79,7 @@ protected function configure(): void
'r',
InputOption::VALUE_REQUIRED,
"Report output format. Available options:\n" .
- '' . \implode(', ', ErrorSuite::getAvaiableRenderFormats()) . '',
+ Utils::printList(ErrorSuite::getAvaiableRenderFormats(), 'info'),
ErrorSuite::REPORT_DEFAULT,
)
->addOption(
@@ -148,7 +148,7 @@ private function getSchemaFilepaths(): array
$schemaFilenames = \array_values(Utils::findFiles($this->getOptArray('schema')));
if (\count($schemaFilenames) === 0) {
- throw new Exception('Schema file(s) not found: ' . \implode('; ', $this->getOptArray('schema')));
+ throw new Exception('Schema file(s) not found: ' . Utils::printList($this->getOptArray('schema')));
}
return $schemaFilenames;
diff --git a/src/Rules/AbstarctRule.php b/src/Rules/AbstarctRule.php
index 6173ca2a..8fe248ce 100644
--- a/src/Rules/AbstarctRule.php
+++ b/src/Rules/AbstarctRule.php
@@ -115,9 +115,9 @@ protected function getOptionAsBool(): bool
{
// TODO: Replace to warning message
if (!\is_bool($this->options)) {
- $options = \is_array($this->options) ? \implode(', ', $this->options) : (string)$this->options;
+ $options = Utils::printList($this->options, 'c');
throw new Exception(
- "Invalid option \"{$options}\" for the \"{$this->getRuleCode()}\" rule. " .
+ "Invalid option {$options} for the \"{$this->getRuleCode()}\" rule. " .
'It should be true|false.',
);
}
@@ -129,10 +129,9 @@ protected function getOptionAsString(): string
{
// TODO: Replace to warning message
if (\is_array($this->options)) {
- $options = \implode(', ', $this->options);
-
+ $options = Utils::printList($this->options, 'c');
throw new Exception(
- "Invalid option \"{$options}\" for the \"{$this->getRuleCode()}\" rule. " .
+ "Invalid option {$options} for the \"{$this->getRuleCode()}\" rule. " .
'It should be int/float/string.',
);
}
@@ -144,9 +143,9 @@ protected function getOptionAsInt(): int
{
// TODO: Replace to warning message
if ($this->options === '' || !\is_numeric($this->options)) {
- $options = \is_array($this->options) ? '[' . \implode(', ', $this->options) . ']' : $this->options;
+ $options = Utils::printList($this->options, 'c');
throw new Exception(
- "Invalid option \"{$options}\" for the \"{$this->getRuleCode()}\" rule. " .
+ "Invalid option {$options} for the \"{$this->getRuleCode()}\" rule. " .
'It should be integer.',
);
}
@@ -158,9 +157,9 @@ protected function getOptionAsFloat(): float
{
// TODO: Replace to warning message
if ($this->options === '' || !\is_numeric($this->options)) {
- $options = \is_array($this->options) ? '[' . \implode(', ', $this->options) . ']' : $this->options;
+ $options = Utils::printList($this->options, 'c');
throw new Exception(
- "Invalid option \"{$options}\" for the \"{$this->getRuleCode()}\" rule. " .
+ "Invalid option {$options} for the \"{$this->getRuleCode()}\" rule. " .
'It should be integer/float.',
);
}
@@ -172,8 +171,9 @@ protected function getOptionAsArray(): array
{
// TODO: Replace to warning message
if (!\is_array($this->options)) {
+ $options = Utils::printList($this->options, 'c');
throw new Exception(
- "Invalid option \"{$this->options}\" for the \"{$this->getRuleCode()}\" rule. " .
+ "Invalid option {$options} for the \"{$this->getRuleCode()}\" rule. " .
'It should be array of strings.',
);
}
diff --git a/src/Rules/Aggregate/ComboPercentile.php b/src/Rules/Aggregate/ComboPercentile.php
index 7d06a8a9..25140e16 100644
--- a/src/Rules/Aggregate/ComboPercentile.php
+++ b/src/Rules/Aggregate/ComboPercentile.php
@@ -41,15 +41,15 @@ public function getHelpMeta(): array
'Similar method used in NumPy and Excel.',
'See: https://en.wikipedia.org/wiki/Percentile#' .
'Second_variant.2C_.7F.27.22.60UNIQ--postMath-00000043-QINU.60.22.27.7F',
- 'Example: `[ 95, 1.234 ]` The 95th percentile in the column must be "1.234" (float).',
+ 'Example: `[ 95.5, 1.234 ]` The 95.5th percentile in the column must be "1.234" (float).',
],
[
- self::MIN => ['[ 95, 1.0 ]', 'x >= 1.0'],
- self::GREATER => ['[ 95, 2.0 ]', 'x > 2.0'],
- self::NOT => ['[ 95, 5.0 ]', 'x != 5.0'],
- self::EQ => ['[ 95, 7.0 ]', 'x == 7.0'],
- self::LESS => ['[ 95, 8.0 ]', 'x < 8.0'],
- self::MAX => ['[ 95, 9.0 ]', 'x <= 9.0'],
+ self::MIN => ['[ 95.0, 1.0 ]', 'x >= 1.0'],
+ self::GREATER => ['[ 95.0, 2.0 ]', 'x > 2.0'],
+ self::NOT => ['[ 95.0, 5.0 ]', 'x != 5.0'],
+ self::EQ => ['[ 95.0, 7.0 ]', 'x == 7.0'],
+ self::LESS => ['[ 95.0, 8.0 ]', 'x < 8.0'],
+ self::MAX => ['[ 95.0, 9.0 ]', 'x <= 9.0'],
],
];
}
diff --git a/src/Rules/Aggregate/ComboQuartiles.php b/src/Rules/Aggregate/ComboQuartiles.php
index e937f433..5824b95c 100644
--- a/src/Rules/Aggregate/ComboQuartiles.php
+++ b/src/Rules/Aggregate/ComboQuartiles.php
@@ -17,6 +17,7 @@
namespace JBZoo\CsvBlueprint\Rules\Aggregate;
use JBZoo\CsvBlueprint\Rules\AbstarctRule;
+use JBZoo\CsvBlueprint\Utils;
use MathPHP\Statistics\Descriptive;
use function JBZoo\Utils\float;
@@ -43,9 +44,9 @@ public function getHelpMeta(): array
'each group comprising a quarter of the data.',
'See: https://en.wikipedia.org/wiki/Quartile',
// Options
- 'There are multiple methods for computing quartiles: "' . \implode('", "', self::METHODS) . '". ' .
+ 'There are multiple methods for computing quartiles: ' . Utils::printList(self::METHODS) . '. ' .
'Exclusive is ussually classic.',
- 'Available types: "' . \implode('", "', self::TYPES) . '" (aka Interquartile Range)',
+ 'Available types: ' . Utils::printList(self::TYPES) . ' ("IQR" is Interquartile Range)',
// Example
'Example: `[ ' . self::METHODS[1] . ", '" . self::TYPES[3] . "', 42.0 ]`" .
' - the ' . self::TYPES[3] . ' ' . self::METHODS[1] . ' quartile is 50.0',
@@ -81,13 +82,11 @@ protected function getActualAggregate(array $colValues): ?float
private function getType(): string
{
- $allowedTypes = ['0%', 'Q1', 'Q2', 'Q3', '100%', 'IQR'];
-
$type = $this->getParams()[self::TYPE];
- if (!\in_array($type, $allowedTypes, true)) {
+ if (!\in_array($type, self::TYPES, true)) {
throw new \RuntimeException(
- "Unknown quartile type: \"{$type}\". Allowed: \"" . \implode('", "', $allowedTypes) . '"',
+ "Unknown quartile type: \"{$type}\". Allowed: " . Utils::printList(self::TYPES, 'green'),
);
}
@@ -96,13 +95,11 @@ private function getType(): string
private function getMethod(): string
{
- $allowedMethods = ['exclusive', 'inclusive'];
-
$method = $this->getParams()[self::METHOD];
- if (!\in_array($method, $allowedMethods, true)) {
+ if (!\in_array($method, self::METHODS, true)) {
throw new \RuntimeException(
- "Unknown quartile method: \"{$method}\". Allowed: \"" . \implode('", "', $allowedMethods) . '"',
+ "Unknown quartile method: \"{$method}\". Allowed: " . Utils::printList(self::METHODS, 'green'),
);
}
@@ -115,7 +112,9 @@ private function getParams(): array
if (\count($params) !== self::ARGS) {
throw new \RuntimeException(
'The rule expects exactly three params: ' .
- 'method (exclusive, inclusive), type (0%, Q1, Q2, Q3, 100%, IQR), expected value (float)',
+ 'method ' . Utils::printList(self::METHODS) . ', ' .
+ 'type ' . Utils::printList(self::TYPES) . ', ' .
+ 'expected value (float)',
);
}
diff --git a/src/Rules/Aggregate/FirstNot.php b/src/Rules/Aggregate/FirstNot.php
index 29dd4098..5fa10d0f 100644
--- a/src/Rules/Aggregate/FirstNot.php
+++ b/src/Rules/Aggregate/FirstNot.php
@@ -28,7 +28,7 @@ public function getHelpMeta(): array
[],
[
self::DEFAULT => [
- "'Not Expected'",
+ 'Not expected',
'Not allowed as the first value in the column. Will be compared as strings.',
],
],
diff --git a/src/Rules/Aggregate/IsSorted.php b/src/Rules/Aggregate/IsSorted.php
index db2794e4..7ec9fe82 100644
--- a/src/Rules/Aggregate/IsSorted.php
+++ b/src/Rules/Aggregate/IsSorted.php
@@ -17,6 +17,7 @@
namespace JBZoo\CsvBlueprint\Rules\Aggregate;
use JBZoo\CsvBlueprint\Rules\AbstarctRule;
+use JBZoo\CsvBlueprint\Utils;
final class IsSorted extends AbstractAggregateRule
{
@@ -39,8 +40,8 @@ public function getHelpMeta(): array
return [
[
'Check if the column is sorted in a specific order.',
- ' - Direction: "' . \implode('", "', self::DIRS) . '".',
- ' - Method: "' . \implode('", "', \array_keys(self::METHODS)) . '".',
+ ' - Direction: ' . Utils::printList(self::DIRS) . '.',
+ ' - Method: ' . Utils::printList(\array_keys(self::METHODS)) . '.',
'See: https://www.php.net/manual/en/function.sort.php',
],
[
@@ -84,7 +85,7 @@ private function getDir(): string
if (!\in_array($dir, self::DIRS, true)) {
throw new \RuntimeException(
- "Unknown sort direction: \"{$dir}\". Allowed: \"" . \implode('", "', self::DIRS) . '"',
+ "Unknown sort direction: \"{$dir}\". Allowed: " . Utils::printList(self::DIRS, 'green'),
);
}
@@ -97,7 +98,7 @@ private function getMethod(): int
if (!\in_array($method, \array_keys(self::METHODS), true)) {
throw new \RuntimeException(
- "Unknown sort method: \"{$method}\". Allowed: \"" . \implode('", "', \array_keys(self::METHODS)) . '"',
+ "Unknown sort method: \"{$method}\". Allowed: " . Utils::printList(\array_keys(self::METHODS), 'green'),
);
}
@@ -110,7 +111,8 @@ private function getParams(): array
if (\count($params) !== self::ARGS) {
throw new \RuntimeException(
'The rule expects exactly two params: ' .
- 'direction ["asc", "desc"] and method ["natural", "regular", "numeric", "string"]',
+ 'direction ' . Utils::printList(self::DIRS) . ' and ' .
+ 'method ' . Utils::printList(\array_keys(self::METHODS)),
);
}
diff --git a/src/Rules/Aggregate/LastNot.php b/src/Rules/Aggregate/LastNot.php
index cceaa5e6..bf24552c 100644
--- a/src/Rules/Aggregate/LastNot.php
+++ b/src/Rules/Aggregate/LastNot.php
@@ -28,7 +28,7 @@ public function getHelpMeta(): array
[],
[
self::DEFAULT => [
- "'Not Expected'",
+ 'Not expected',
'Not allowed as the last value in the column. Will be compared as strings.',
],
],
diff --git a/src/Rules/Aggregate/NthNot.php b/src/Rules/Aggregate/NthNot.php
index 4ee4129f..3e22db24 100644
--- a/src/Rules/Aggregate/NthNot.php
+++ b/src/Rules/Aggregate/NthNot.php
@@ -32,7 +32,7 @@ public function getHelpMeta(): array
[],
[
self::DEFAULT => [
- "[ 2, 'Not expected' ]",
+ '[ 2, Not expected ]',
'Not allowed as the N-th value in the column. Will be compared as strings.',
],
],
diff --git a/src/Rules/Cell/AllowValues.php b/src/Rules/Cell/AllowValues.php
index 1049799a..633adc7e 100644
--- a/src/Rules/Cell/AllowValues.php
+++ b/src/Rules/Cell/AllowValues.php
@@ -16,6 +16,8 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use JBZoo\CsvBlueprint\Utils;
+
class AllowValues extends AbstractCellRule
{
public function getHelpMeta(): array
@@ -36,7 +38,7 @@ public function validateRule(string $cellValue): ?string
if (!\in_array($cellValue, $allowedValues, true)) {
return "Value \"{$cellValue}\" is not allowed. " .
- 'Allowed values: ["' . \implode('", "', $allowedValues) . '"]';
+ 'Allowed values: ' . Utils::printList($allowedValues, 'green');
}
return null;
diff --git a/src/Rules/Cell/Contains.php b/src/Rules/Cell/Contains.php
index 374c2a96..468dc38c 100644
--- a/src/Rules/Cell/Contains.php
+++ b/src/Rules/Cell/Contains.php
@@ -22,7 +22,7 @@ public function getHelpMeta(): array
{
return [
[],
- [self::DEFAULT => ['Hello', 'Example: "Hello World".']],
+ [self::DEFAULT => ['World', 'Example: "Hello World!". The string must contain "World" in any place.']],
];
}
diff --git a/src/Rules/Cell/ContainsAll.php b/src/Rules/Cell/ContainsAll.php
index aedc1a31..5033a2f6 100644
--- a/src/Rules/Cell/ContainsAll.php
+++ b/src/Rules/Cell/ContainsAll.php
@@ -16,13 +16,15 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use JBZoo\CsvBlueprint\Utils;
+
final class ContainsAll extends AbstractCellRule
{
public function getHelpMeta(): array
{
return [
[],
- [self::DEFAULT => ['[ a, b, c ]', 'All the strings must be part of a CSV value.']],
+ [self::DEFAULT => ['[ a, b ]', 'All the strings must be part of a CSV value.']],
];
}
@@ -39,8 +41,8 @@ public function validateRule(string $cellValue): ?string
foreach ($inclusions as $inclusion) {
if (\strpos($cellValue, $inclusion) === false) {
- return "Value \"{$cellValue}\" must contain all of the following:" .
- ' "["' . \implode('", "', $inclusions) . '"]"';
+ return "Value \"{$cellValue}\" must contain all of the following: "
+ . Utils::printList($inclusions, 'green');
}
}
diff --git a/src/Rules/Cell/ContainsAny.php b/src/Rules/Cell/ContainsAny.php
new file mode 100644
index 00000000..39a92aae
--- /dev/null
+++ b/src/Rules/Cell/ContainsAny.php
@@ -0,0 +1,51 @@
+ ['[ a, b ]', 'At least one of the string must be part of the CSV value.']],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if ($cellValue === '') {
+ return null;
+ }
+
+ $inclusions = $this->getOptionAsArray();
+ if (\count($inclusions) === 0) {
+ return 'Rule must contain at least one inclusion value in schema file.';
+ }
+
+ foreach ($inclusions as $inclusion) {
+ if (\strpos($cellValue, $inclusion) !== false) {
+ return null;
+ }
+ }
+
+ return "Value \"{$cellValue}\" must contain at least one of the following: " .
+ Utils::printList($inclusions, 'green');
+ }
+}
diff --git a/src/Rules/Cell/ContainsNone.php b/src/Rules/Cell/ContainsNone.php
index 15316b33..43a50af6 100644
--- a/src/Rules/Cell/ContainsNone.php
+++ b/src/Rules/Cell/ContainsNone.php
@@ -16,6 +16,8 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use JBZoo\CsvBlueprint\Utils;
+
final class ContainsNone extends AbstractCellRule
{
public function getHelpMeta(): array
@@ -39,8 +41,8 @@ public function validateRule(string $cellValue): ?string
foreach ($exclusions as $exclusion) {
if (\strpos($cellValue, $exclusion) !== false) {
- return "Value \"{$cellValue}\" must not contain any of the following:" .
- ' "["' . \implode('", "', $exclusions) . '"]"';
+ return "Value \"{$cellValue}\" must not contain any of the following: " .
+ Utils::printList($exclusions, 'green');
}
}
diff --git a/src/Rules/Cell/ContainsOne.php b/src/Rules/Cell/ContainsOne.php
index 7961133d..41afdc5e 100644
--- a/src/Rules/Cell/ContainsOne.php
+++ b/src/Rules/Cell/ContainsOne.php
@@ -16,13 +16,15 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use JBZoo\CsvBlueprint\Utils;
+
final class ContainsOne extends AbstractCellRule
{
public function getHelpMeta(): array
{
return [
[],
- [self::DEFAULT => ['[ a, b ]', 'At least one of the string must be part of the CSV value.']],
+ [self::DEFAULT => ['[ a, b ]', 'Only one of the strings must be part of the CSV value.']],
];
}
@@ -37,13 +39,18 @@ public function validateRule(string $cellValue): ?string
return 'Rule must contain at least one inclusion value in schema file.';
}
+ $found = 0;
foreach ($inclusions as $inclusion) {
if (\strpos($cellValue, $inclusion) !== false) {
- return null;
+ $found++;
}
}
- return "Value \"{$cellValue}\" must contain at least one of the following:" .
- ' "["' . \implode('", "', $inclusions) . '"]"';
+ if ($found === 1) {
+ return null;
+ }
+
+ return "Value \"{$cellValue}\" must contain exactly one of the following: " .
+ Utils::printList($inclusions, 'green');
}
}
diff --git a/src/Rules/Cell/IpV4Range.php b/src/Rules/Cell/IpV4Range.php
new file mode 100644
index 00000000..ada1c97d
--- /dev/null
+++ b/src/Rules/Cell/IpV4Range.php
@@ -0,0 +1,54 @@
+ [
+ "[ '127.0.0.1-127.0.0.5', '127.0.0.0/21' ]",
+ 'Check subnet mask or range for IPv4. Address must be in one of the ranges.',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ $ranges = $this->getOptionAsArray();
+
+ if (\count($ranges) === 0) {
+ return 'IPv4 range is not defined.';
+ }
+
+ foreach ($ranges as $range) {
+ if (Validator::ip($range, \FILTER_FLAG_IPV4)->validate($cellValue)) {
+ return null;
+ }
+ }
+
+ return "Value \"{$cellValue}\" is not included in any of IPv4 the ranges: " .
+ Utils::printList($ranges, 'green');
+ }
+}
diff --git a/src/Rules/Cell/IsCardinalDirection.php b/src/Rules/Cell/IsCardinalDirection.php
index 69f7c18f..620498e4 100644
--- a/src/Rules/Cell/IsCardinalDirection.php
+++ b/src/Rules/Cell/IsCardinalDirection.php
@@ -16,6 +16,8 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use JBZoo\CsvBlueprint\Utils;
+
final class IsCardinalDirection extends AllowValues
{
public function getHelpMeta(): array
@@ -25,7 +27,7 @@ public function getHelpMeta(): array
[
self::DEFAULT => [
'true',
- 'Valid cardinal direction. Available values: "' . \implode('", "', $this->getOptionAsArray()) . '"',
+ 'Valid cardinal direction. Available values: ' . Utils::printList($this->getOptionAsArray()),
],
],
];
diff --git a/src/Rules/Cell/CountryCode.php b/src/Rules/Cell/IsCountryCode.php
similarity index 90%
rename from src/Rules/Cell/CountryCode.php
rename to src/Rules/Cell/IsCountryCode.php
index 2d17cd50..ecad274e 100644
--- a/src/Rules/Cell/CountryCode.php
+++ b/src/Rules/Cell/IsCountryCode.php
@@ -16,10 +16,11 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use JBZoo\CsvBlueprint\Utils;
use Respect\Validation\Rules\CountryCode as RespectCountryCode;
use Respect\Validation\Validator;
-class CountryCode extends AbstractCellRule
+class IsCountryCode extends AbstractCellRule
{
public function getHelpMeta(): array
{
@@ -51,11 +52,11 @@ public function validateRule(string $cellValue): ?string
if (!\in_array($set, $validSets, true)) {
return "Unknown country set: \"{$set}\". " .
- 'Available options: [' . \implode(', ', $validSets) . ']';
+ 'Available options: ' . Utils::printList($validSets, 'green');
}
if (!Validator::countryCode($set)->validate($cellValue)) {
- return "Value \"{$cellValue}\" is not a valid {$set} country code.";
+ return "Value \"{$cellValue}\" is not a valid \"{$set}\" country code.";
}
return null;
diff --git a/src/Rules/Cell/IsDirExists.php b/src/Rules/Cell/IsDirExists.php
new file mode 100644
index 00000000..cd9ddb6f
--- /dev/null
+++ b/src/Rules/Cell/IsDirExists.php
@@ -0,0 +1,39 @@
+ ['true', "Check if directory exists on the filesystem (It's FS IO operation!)."],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if (!\file_exists($cellValue)) {
+ return "Directory \"{$cellValue}\" not found";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsDomain.php b/src/Rules/Cell/IsDomain.php
index a4ede646..3a6d2f4e 100644
--- a/src/Rules/Cell/IsDomain.php
+++ b/src/Rules/Cell/IsDomain.php
@@ -16,7 +16,7 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
-use JBZoo\CsvBlueprint\Utils;
+use Respect\Validation\Validator;
final class IsDomain extends AbstractCellRule
{
@@ -32,9 +32,7 @@ public function getHelpMeta(): array
public function validateRule(string $cellValue): ?string
{
- $domainPattern = '/^(?!-)[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$/';
-
- if (Utils::testRegex($domainPattern, $cellValue)) {
+ if (!Validator::domain()->validate($cellValue)) {
return "Value \"{$cellValue}\" is not a valid domain";
}
diff --git a/src/Rules/Cell/IsEmail.php b/src/Rules/Cell/IsEmail.php
index cb8d919c..ab51e8d4 100644
--- a/src/Rules/Cell/IsEmail.php
+++ b/src/Rules/Cell/IsEmail.php
@@ -16,6 +16,8 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use Respect\Validation\Validator;
+
final class IsEmail extends AbstractCellRule
{
public function getHelpMeta(): array
@@ -30,7 +32,7 @@ public function getHelpMeta(): array
public function validateRule(string $cellValue): ?string
{
- if (\filter_var($cellValue, \FILTER_VALIDATE_EMAIL) === false) {
+ if (!Validator::email()->validate($cellValue)) {
return "Value \"{$cellValue}\" is not a valid email";
}
diff --git a/src/Rules/Cell/IsFileExists.php b/src/Rules/Cell/IsFileExists.php
new file mode 100644
index 00000000..6252106e
--- /dev/null
+++ b/src/Rules/Cell/IsFileExists.php
@@ -0,0 +1,39 @@
+ ['true', "Check if file exists on the filesystem (It's FS IO operation!)."],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if (!\file_exists($cellValue)) {
+ return "File \"{$cellValue}\" not found";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsIp.php b/src/Rules/Cell/IsIp.php
new file mode 100644
index 00000000..5965de64
--- /dev/null
+++ b/src/Rules/Cell/IsIp.php
@@ -0,0 +1,41 @@
+ ['true', 'Both: IPv4 or IPv6.'],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if (!Validator::ip('*')->validate($cellValue)) {
+ return "Value \"{$cellValue}\" is not a valid IPv6 or IPv4";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsIpPrivate.php b/src/Rules/Cell/IsIpPrivate.php
new file mode 100644
index 00000000..f998fa21
--- /dev/null
+++ b/src/Rules/Cell/IsIpPrivate.php
@@ -0,0 +1,45 @@
+ [
+ 'true',
+ 'IPv4 has ranges: 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16. ' .
+ 'IPv6 has ranges starting with FD or FC.',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if (Validator::ip('*', \FILTER_FLAG_NO_PRIV_RANGE)->validate($cellValue)) {
+ return "Value \"{$cellValue}\" is not a private IP address.";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsIpReserved.php b/src/Rules/Cell/IsIpReserved.php
new file mode 100644
index 00000000..02f80647
--- /dev/null
+++ b/src/Rules/Cell/IsIpReserved.php
@@ -0,0 +1,45 @@
+ [
+ 'true',
+ 'IPv4 has ranges: 0.0.0.0/8, 169.254.0.0/16, 127.0.0.0/8 and 240.0.0.0/4. ' .
+ 'IPv6 has ranges: ::1/128, ::/128, ::ffff:0:0/96 and fe80::/10.',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if (Validator::ip('*', \FILTER_FLAG_NO_RES_RANGE)->validate($cellValue)) {
+ return "Value \"{$cellValue}\" is not a reserved IP address.";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsIp4.php b/src/Rules/Cell/IsIpV4.php
similarity index 81%
rename from src/Rules/Cell/IsIp4.php
rename to src/Rules/Cell/IsIpV4.php
index 2218848a..ef3b05d2 100644
--- a/src/Rules/Cell/IsIp4.php
+++ b/src/Rules/Cell/IsIpV4.php
@@ -16,22 +16,24 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
-final class IsIp4 extends AbstractCellRule
+use Respect\Validation\Validator;
+
+final class IsIpV4 extends AbstractCellRule
{
public function getHelpMeta(): array
{
return [
[],
[
- self::DEFAULT => ['true', 'Only IPv4. Example: "127.0.0.1"'],
+ self::DEFAULT => ['true', 'Only IPv4. Example: "127.0.0.1".'],
],
];
}
public function validateRule(string $cellValue): ?string
{
- if (\filter_var($cellValue, \FILTER_VALIDATE_IP) === false) {
- return "Value \"{$cellValue}\" is not a valid IP";
+ if (!Validator::ip('*', \FILTER_FLAG_IPV4)->validate($cellValue)) {
+ return "Value \"{$cellValue}\" is not a valid IPv4";
}
return null;
diff --git a/src/Rules/Cell/IsIpV6.php b/src/Rules/Cell/IsIpV6.php
new file mode 100644
index 00000000..61e3b93a
--- /dev/null
+++ b/src/Rules/Cell/IsIpV6.php
@@ -0,0 +1,41 @@
+ ['true', 'Only IPv6. Example: "2001:0db8:85a3:08d3:1319:8a2e:0370:7334".'],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if (!Validator::ip('*', \FILTER_FLAG_IPV6)->validate($cellValue)) {
+ return "Value \"{$cellValue}\" is not a valid IPv6";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/LanguageCode.php b/src/Rules/Cell/IsLanguageCode.php
similarity index 89%
rename from src/Rules/Cell/LanguageCode.php
rename to src/Rules/Cell/IsLanguageCode.php
index e081b2fd..38d68ec6 100644
--- a/src/Rules/Cell/LanguageCode.php
+++ b/src/Rules/Cell/IsLanguageCode.php
@@ -16,10 +16,11 @@
namespace JBZoo\CsvBlueprint\Rules\Cell;
+use JBZoo\CsvBlueprint\Utils;
use Respect\Validation\Rules\LanguageCode as RespectLanguageCode;
use Respect\Validation\Validator;
-class LanguageCode extends AbstractCellRule
+class IsLanguageCode extends AbstractCellRule
{
public function getHelpMeta(): array
{
@@ -47,11 +48,11 @@ public function validateRule(string $cellValue): ?string
if (!\in_array($set, $validSets, true)) {
return "Unknown language set: \"{$set}\". " .
- 'Available options: [' . \implode(', ', $validSets) . ']';
+ 'Available options: ' . Utils::printList($validSets, 'green');
}
if (!Validator::languageCode($set)->validate($cellValue)) {
- return "Value \"{$cellValue}\" is not a valid {$set} language code.";
+ return "Value \"{$cellValue}\" is not a valid \"{$set}\" language code.";
}
return null;
diff --git a/src/Rules/Cell/IsMacAddress.php b/src/Rules/Cell/IsMacAddress.php
new file mode 100644
index 00000000..e1d4f091
--- /dev/null
+++ b/src/Rules/Cell/IsMacAddress.php
@@ -0,0 +1,41 @@
+ ['true', 'The input is a valid MAC address. Example: 00:00:5e:00:53:01'],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ if (!Validator::macAddress()->validate($cellValue)) {
+ return "Value \"{$cellValue}\" is not a valid MAC address.";
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/Cell/IsPublicDomainSuffix.php b/src/Rules/Cell/IsPublicDomainSuffix.php
new file mode 100644
index 00000000..4d838ab9
--- /dev/null
+++ b/src/Rules/Cell/IsPublicDomainSuffix.php
@@ -0,0 +1,46 @@
+ [
+ 'true',
+ 'The input is a public ICANN domain suffix. Example: "com", "nom.br", "net" etc.',
+ ],
+ ],
+ ];
+ }
+
+ public function validateRule(string $cellValue): ?string
+ {
+ // @phpstan-ignore-next-line
+ if (!Validator::oneOf(Validator::tld(), Validator::publicDomainSuffix())->validate($cellValue)) {
+ return "The value \"{$cellValue}\" is not a valid public domain suffix. " .
+ 'Example: "com", "nom.br", "net" etc.';
+ }
+
+ return null;
+ }
+}
diff --git a/src/Rules/DocBuilder.php b/src/Rules/DocBuilder.php
index 27f9ba81..9b3dd37c 100644
--- a/src/Rules/DocBuilder.php
+++ b/src/Rules/DocBuilder.php
@@ -71,6 +71,7 @@ final class DocBuilder
private const HELP_LEFT_PAD = 6;
private const HELP_DESC_PAD = 40;
+ private const HELP_DESC_PAD_BIG = 60;
private array $topHelp;
private array $options;
@@ -148,11 +149,11 @@ private static function renderLine(string $ruleCode, array $row, string $mode):
: "{$leftPad}{$ruleCode}_{$mode}: {$row[0]}";
if (\strlen($baseKeyVal) > $descPad) {
- $descPad = 60;
+ $descPad = self::HELP_DESC_PAD_BIG;
}
if (isset($row[1]) && $row[1] !== '') {
- return \str_pad($baseKeyVal, $descPad, ' ', \STR_PAD_RIGHT) . "# {$row[1]}";
+ return \str_pad($baseKeyVal, $descPad - 1, ' ', \STR_PAD_RIGHT) . " # {$row[1]}";
}
return $baseKeyVal;
diff --git a/src/Utils.php b/src/Utils.php
index 70ad6f94..54aef887 100644
--- a/src/Utils.php
+++ b/src/Utils.php
@@ -29,6 +29,31 @@ final class Utils
{
public const MAX_DIRECTORY_DEPTH = 10;
+ public static function printList(null|array|bool|float|int|string $items, string $color = ''): string
+ {
+ if (!\is_array($items)) {
+ $items = [$items];
+ }
+
+ if (\count($items) === 0) {
+ return '[]';
+ }
+
+ if (\count($items) === 1) {
+ $val = \reset($items);
+ if ($color === '') {
+ return "\"{$val}\"";
+ }
+ return "\"<{$color}>{$val}{$color}>\"";
+ }
+
+ if ($color === '') {
+ return '["' . \implode('", "', $items) . '"]';
+ }
+
+ return "[\"<{$color}>" . \implode("{$color}>\", \"<{$color}>", $items) . "{$color}>\"]";
+ }
+
public static function debug(int|string $message): void
{
if (\defined('PROFILE_MODE')) {
@@ -207,7 +232,7 @@ public static function matchTypes(
'array' => [],
'boolean' => [],
'double' => ['string', 'integer'],
- 'integer' => ['string', 'double'],
+ 'integer' => [],
'string' => ['double', 'integer'],
];
diff --git a/src/Validators/ErrorSuite.php b/src/Validators/ErrorSuite.php
index 97dea338..579a4a12 100644
--- a/src/Validators/ErrorSuite.php
+++ b/src/Validators/ErrorSuite.php
@@ -137,10 +137,6 @@ private function renderPlainText(): string
$result[] = (string)$error;
}
- // if (\count($result) > 0) {
- // \array_unshift($result, $this->csvFilename);
- // }
-
return \implode("\n", $result) . "\n";
}
diff --git a/src/Validators/ValidatorCsv.php b/src/Validators/ValidatorCsv.php
index 50a6bf23..37ef0dc9 100644
--- a/src/Validators/ValidatorCsv.php
+++ b/src/Validators/ValidatorCsv.php
@@ -208,7 +208,7 @@ private function validateColumn(bool $quickStop): ErrorSuite
if (\count($notFoundColums) > 0) {
$error = new Error(
'csv.header',
- 'Columns not found in CSV: "' . \implode(', ', $notFoundColums) . '"',
+ 'Columns not found in CSV: ' . Utils::printList($notFoundColums, 'c'),
'',
ValidatorColumn::FALLBACK_LINE,
);
diff --git a/tests/Commands/ValidateCsvBasicTest.php b/tests/Commands/ValidateCsvBasicTest.php
index ef424ba9..e3882fce 100644
--- a/tests/Commands/ValidateCsvBasicTest.php
+++ b/tests/Commands/ValidateCsvBasicTest.php
@@ -302,7 +302,7 @@ public function testValidateOneCsvNoHeaderNegative(): void
public function testSchemaNotFound(): void
{
- $this->expectExceptionMessage('Schema file(s) not found: invalid_schema_path.yml');
+ $this->expectExceptionMessage('Schema file(s) not found: "invalid_schema_path.yml"');
Tools::virtualExecution('validate:csv', [
'csv' => './tests/fixtures/no-found-file.csv',
'schema' => 'invalid_schema_path.yml',
diff --git a/tests/Rules/Aggregate/ComboAverageTest.php b/tests/Rules/Aggregate/ComboAverageTest.php
index eed38160..6e131941 100644
--- a/tests/Rules/Aggregate/ComboAverageTest.php
+++ b/tests/Rules/Aggregate/ComboAverageTest.php
@@ -79,7 +79,7 @@ public function testInvalidOption(): void
$rule = $this->create([1, 2], Combo::MAX);
isSame(
'"ag:average_max" at line 1, column "prop". ' .
- 'Invalid option "[1, 2]" for the "ag:average_max" rule. ' .
+ 'Invalid option ["1", "2"] for the "ag:average_max" rule. ' .
'It should be integer/float.',
(string)$rule->validate(['1', '2', '3']),
);
diff --git a/tests/Rules/Aggregate/ComboCountEmptyTest.php b/tests/Rules/Aggregate/ComboCountEmptyTest.php
index 5f77c036..0ae2ffed 100644
--- a/tests/Rules/Aggregate/ComboCountEmptyTest.php
+++ b/tests/Rules/Aggregate/ComboCountEmptyTest.php
@@ -85,7 +85,7 @@ public function testInvalidOption(): void
$rule = $this->create([1, 2], Combo::MAX);
isSame(
'"ag:count_empty_max" at line 1, column "prop". ' .
- 'Invalid option "[1, 2]" for the "ag:count_empty_max" rule. ' .
+ 'Invalid option ["1", "2"] for the "ag:count_empty_max" rule. ' .
'It should be integer/float.',
(string)$rule->validate(['1', '2', '3']),
);
diff --git a/tests/Rules/Aggregate/ComboQuartilesTest.php b/tests/Rules/Aggregate/ComboQuartilesTest.php
index f5b2bb21..79351e5a 100644
--- a/tests/Rules/Aggregate/ComboQuartilesTest.php
+++ b/tests/Rules/Aggregate/ComboQuartilesTest.php
@@ -79,19 +79,21 @@ public function testInvalidOption(): void
$rule = $this->create([950.05], Combo::EQ);
isSame(
'The rule expects exactly three params: ' .
- 'method (exclusive, inclusive), type (0%, Q1, Q2, Q3, 100%, IQR), expected value (float)',
+ 'method ["exclusive", "inclusive"], ' .
+ 'type ["0%", "Q1", "Q2", "Q3", "100%", "IQR"], ' .
+ 'expected value (float)',
$rule->test(\range(1, 200)),
);
$rule = $this->create(['qwerty', 'IQR', 5], Combo::EQ);
isSame(
- 'Unknown quartile method: "qwerty". Allowed: "exclusive", "inclusive"',
+ 'Unknown quartile method: "qwerty". Allowed: ["exclusive", "inclusive"]',
$rule->test(\range(1, 200)),
);
$rule = $this->create(['inclusive', 'QQQQ', 5], Combo::EQ);
isSame(
- 'Unknown quartile type: "QQQQ". Allowed: "0%", "Q1", "Q2", "Q3", "100%", "IQR"',
+ 'Unknown quartile type: "QQQQ". Allowed: ["0%", "Q1", "Q2", "Q3", "100%", "IQR"]',
$rule->test(\range(1, 200)),
);
}
diff --git a/tests/Rules/Aggregate/IsSortedTest.php b/tests/Rules/Aggregate/IsSortedTest.php
index a9f98dfe..968b38f2 100644
--- a/tests/Rules/Aggregate/IsSortedTest.php
+++ b/tests/Rules/Aggregate/IsSortedTest.php
@@ -67,13 +67,13 @@ public function testNegative(): void
$rule = $this->create(['QQQ', 'natural']);
isSame(
- 'Unknown sort direction: "QQQ". Allowed: "asc", "desc"',
+ 'Unknown sort direction: "QQQ". Allowed: ["asc", "desc"]',
$rule->test(['1', '11', '2', '20', '21']),
);
$rule = $this->create(['asc', 'QQQQQQQ']);
isSame(
- 'Unknown sort method: "QQQQQQQ". Allowed: "natural", "regular", "numeric", "string"',
+ 'Unknown sort method: "QQQQQQQ". Allowed: ["natural", "regular", "numeric", "string"]',
$rule->test(['1', '11', '2', '20', '21']),
);
diff --git a/tests/Rules/Cell/AllowValuesTest.php b/tests/Rules/Cell/AllowValuesTest.php
index 12099de8..236f8f99 100644
--- a/tests/Rules/Cell/AllowValuesTest.php
+++ b/tests/Rules/Cell/AllowValuesTest.php
@@ -59,7 +59,8 @@ public function testInvalidOption(): void
$rule = $this->create('qwe');
isSame(
'"allow_values" at line 1, column "prop". ' .
- 'Unexpected error: Invalid option "qwe" for the "allow_values" rule. It should be array of strings.',
+ 'Unexpected error: Invalid option "qwe" for the "allow_values" rule. ' .
+ 'It should be array of strings.',
(string)$rule->validate('true'),
);
}
diff --git a/tests/Rules/Cell/ComboLengthTest.php b/tests/Rules/Cell/ComboLengthTest.php
index e44317e0..b31d480a 100644
--- a/tests/Rules/Cell/ComboLengthTest.php
+++ b/tests/Rules/Cell/ComboLengthTest.php
@@ -85,10 +85,12 @@ public function testMax(): void
public function testInvalidOption(): void
{
$this->expectException(\JBZoo\CsvBlueprint\Rules\Exception::class);
- $this->expectExceptionMessage('Invalid option "qwerty" for the "length_max" rule. It should be integer.');
+ $this->expectExceptionMessage(
+ 'Invalid option "qwerty" for the "length_max" rule. It should be integer.',
+ );
$rule = $this->create('qwerty', Combo::MAX);
- $rule->test('12345');
+ $rule->validate('12345');
}
public function testInvalidParsing(): void
diff --git a/tests/Rules/Cell/ComboPrecisionTest.php b/tests/Rules/Cell/ComboPrecisionTest.php
index 09be781e..9e4f40a9 100644
--- a/tests/Rules/Cell/ComboPrecisionTest.php
+++ b/tests/Rules/Cell/ComboPrecisionTest.php
@@ -81,7 +81,9 @@ public function testNotEqual(): void
public function testInvalidOption(): void
{
- $this->expectExceptionMessage('Invalid option "s.223" for the "precision_not" rule. It should be integer.');
+ $this->expectExceptionMessage(
+ 'Invalid option "s.223" for the "precision_not" rule. It should be integer.',
+ );
$rule = $this->create('s.223', Combo::NOT);
isSame('', $rule->test('5'));
}
diff --git a/tests/Rules/Cell/ComboTest.php b/tests/Rules/Cell/ComboTest.php
index cf12fd6e..18d5363f 100644
--- a/tests/Rules/Cell/ComboTest.php
+++ b/tests/Rules/Cell/ComboTest.php
@@ -127,7 +127,8 @@ public function testInvalidParsing(): void
public function testInvalidOption2(): void
{
$this->expectExceptionMessage(
- 'Invalid option "1, 2, 3" for the "num_not" rule. It should be int/float/string.',
+ 'Invalid option ["1", "2", "3"] for the "num_not" rule. ' .
+ 'It should be int/float/string.',
);
$rule = $this->create([1, 2, 3], Combo::NOT);
diff --git a/tests/Rules/Cell/ComboWordCountTest.php b/tests/Rules/Cell/ComboWordCountTest.php
index 119112d8..747ce0ad 100644
--- a/tests/Rules/Cell/ComboWordCountTest.php
+++ b/tests/Rules/Cell/ComboWordCountTest.php
@@ -21,7 +21,6 @@
use JBZoo\PHPUnit\Rules\TestAbstractCellRuleCombo;
use function JBZoo\PHPUnit\isSame;
-use function JBZoo\PHPUnit\success;
class ComboWordCountTest extends TestAbstractCellRuleCombo
{
@@ -98,18 +97,4 @@ public function testMax(): void
$rule->test('asd, asdasd asd 1232 asdas'),
);
}
-
- public function testInvalidOption(): void
- {
- $this->expectException(\JBZoo\CsvBlueprint\Rules\Exception::class);
- $this->expectExceptionMessage('Invalid option "qwerty" for the "word_count_max" rule. It should be integer.');
-
- $rule = $this->create('qwerty', Combo::MAX);
- $rule->test('12345');
- }
-
- public function testInvalidParsing(): void
- {
- success('No cases for invalid parsing.');
- }
}
diff --git a/tests/Rules/Cell/ContainsAllTest.php b/tests/Rules/Cell/ContainsAllTest.php
index ddf4817c..091eb961 100644
--- a/tests/Rules/Cell/ContainsAllTest.php
+++ b/tests/Rules/Cell/ContainsAllTest.php
@@ -45,11 +45,11 @@ public function testNegative(): void
$rule = $this->create(['a', 'b', 'c']);
isSame(
- 'Value "ab" must contain all of the following: "["a", "b", "c"]"',
+ 'Value "ab" must contain all of the following: ["a", "b", "c"]',
$rule->test('ab'),
);
isSame(
- 'Value "ac" must contain all of the following: "["a", "b", "c"]"',
+ 'Value "ac" must contain all of the following: ["a", "b", "c"]',
$rule->test('ac'),
);
}
diff --git a/tests/Rules/Cell/ContainsAnyTest.php b/tests/Rules/Cell/ContainsAnyTest.php
new file mode 100644
index 00000000..1069ca56
--- /dev/null
+++ b/tests/Rules/Cell/ContainsAnyTest.php
@@ -0,0 +1,54 @@
+create([]);
+ isSame('', $rule->test(''));
+
+ $rule = $this->create(['a', 'b', 'c']);
+ isSame('', $rule->test('a'));
+ isSame('', $rule->test('ab'));
+ isSame('', $rule->test('abc'));
+ isSame('', $rule->test('abc '));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create([]);
+ isSame(
+ 'Rule must contain at least one inclusion value in schema file.',
+ $rule->test('ac'),
+ );
+
+ $rule = $this->create(['a', 'b', 'c']);
+ isSame(
+ 'Value "d" must contain at least one of the following: ["a", "b", "c"]',
+ $rule->test('d'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/ContainsNoneTest.php b/tests/Rules/Cell/ContainsNoneTest.php
index 825e92fd..6e83d742 100644
--- a/tests/Rules/Cell/ContainsNoneTest.php
+++ b/tests/Rules/Cell/ContainsNoneTest.php
@@ -45,13 +45,13 @@ public function testNegative(): void
$rule = $this->create(['a', 'b', 'c']);
isSame(
- 'Value "a" must not contain any of the following: "["a", "b", "c"]"',
+ 'Value "a" must not contain any of the following: ["a", "b", "c"]',
$rule->test('a'),
);
$rule = $this->create(['a', 'b', 'c']);
isSame(
- 'Value "ddddb" must not contain any of the following: "["a", "b", "c"]"',
+ 'Value "ddddb" must not contain any of the following: ["a", "b", "c"]',
$rule->test('ddddb'),
);
}
diff --git a/tests/Rules/Cell/ContainsOneTest.php b/tests/Rules/Cell/ContainsOneTest.php
index 21bae4e5..fe055a00 100644
--- a/tests/Rules/Cell/ContainsOneTest.php
+++ b/tests/Rules/Cell/ContainsOneTest.php
@@ -32,9 +32,8 @@ public function testPositive(): void
$rule = $this->create(['a', 'b', 'c']);
isSame('', $rule->test('a'));
- isSame('', $rule->test('ab'));
- isSame('', $rule->test('abc'));
- isSame('', $rule->test('abc '));
+ isSame('', $rule->test('b'));
+ isSame('', $rule->test('c'));
}
public function testNegative(): void
@@ -47,8 +46,14 @@ public function testNegative(): void
$rule = $this->create(['a', 'b', 'c']);
isSame(
- 'Value "d" must contain at least one of the following: "["a", "b", "c"]"',
+ 'Value "d" must contain exactly one of the following: ["a", "b", "c"]',
$rule->test('d'),
);
+
+ $rule = $this->create(['a', 'b', 'c']);
+ isSame(
+ 'Value "ab" must contain exactly one of the following: ["a", "b", "c"]',
+ $rule->test('ab'),
+ );
}
}
diff --git a/tests/Rules/Cell/IpV4RangeTest.php b/tests/Rules/Cell/IpV4RangeTest.php
new file mode 100644
index 00000000..f83f0d6c
--- /dev/null
+++ b/tests/Rules/Cell/IpV4RangeTest.php
@@ -0,0 +1,71 @@
+create(['127.0.0.1-127.0.0.5', '127.0.0.0/21']);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test('127.0.0.0'));
+ isSame('', $rule->test('127.0.0.5'));
+ isSame('', $rule->test('127.0.0.5'));
+ isSame('', $rule->test('127.0.7.255'));
+
+ $rule = $this->create(['127.0.0.1-127.0.0.5']);
+ isSame('', $rule->test('127.0.0.1'));
+ isSame('', $rule->test('127.0.0.2'));
+ isSame('', $rule->test('127.0.0.5'));
+
+ $rule = $this->create(['127.0.0.0/21']);
+ isSame('', $rule->test('127.0.1.1'));
+ isSame('', $rule->test('127.0.7.255'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(['127.0.0.1-127.0.0.5']);
+ isSame(
+ 'Value "1.2.3" is not included in any of IPv4 the ranges: "127.0.0.1-127.0.0.5"',
+ $rule->test('1.2.3'),
+ );
+ isSame(
+ '"ip_v4_range" at line 1, column "prop". ' .
+ 'Value "1.2.3" is not included in any of IPv4 the ranges: "127.0.0.1-127.0.0.5".',
+ (string)$rule->validate('1.2.3'),
+ );
+ isSame(
+ 'Value "2001:0db8:85a3:08d3:1319:8a2e:0370:7334" is not included in any of IPv4 the ranges: ' .
+ '"127.0.0.1-127.0.0.5"',
+ $rule->test('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'),
+ );
+
+ $rule = $this->create([]);
+ isSame(
+ 'IPv4 range is not defined.',
+ $rule->test('127.0.0.1'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/IsBoolTest.php b/tests/Rules/Cell/IsBoolTest.php
index f0b6c5e7..4f9d4f43 100644
--- a/tests/Rules/Cell/IsBoolTest.php
+++ b/tests/Rules/Cell/IsBoolTest.php
@@ -54,12 +54,4 @@ public function testNegative(): void
$rule->test(''),
);
}
-
- public function testInvalidOption(): void
- {
- $this->expectExceptionMessage('Invalid option "qwerty" for the "is_bool" rule. It should be true|false.');
-
- $rule = $this->create('qwerty');
- $rule->validate('true');
- }
}
diff --git a/tests/Rules/Cell/CountryCodeTest.php b/tests/Rules/Cell/IsCountryCodeTest.php
similarity index 80%
rename from tests/Rules/Cell/CountryCodeTest.php
rename to tests/Rules/Cell/IsCountryCodeTest.php
index af16b643..38106645 100644
--- a/tests/Rules/Cell/CountryCodeTest.php
+++ b/tests/Rules/Cell/IsCountryCodeTest.php
@@ -16,15 +16,15 @@
namespace JBZoo\PHPUnit\Rules\Cell;
-use JBZoo\CsvBlueprint\Rules\Cell\CountryCode;
+use JBZoo\CsvBlueprint\Rules\Cell\IsCountryCode;
use JBZoo\PHPUnit\Rules\TestAbstractCellRule;
use Respect\Validation\Rules\CountryCode as RespectCountryCode;
use function JBZoo\PHPUnit\isSame;
-final class CountryCodeTest extends TestAbstractCellRule
+final class IsCountryCodeTest extends TestAbstractCellRule
{
- protected string $ruleClass = CountryCode::class;
+ protected string $ruleClass = IsCountryCode::class;
public function testPositive(): void
{
@@ -43,19 +43,19 @@ public function testNegative(): void
{
$rule = $this->create(RespectCountryCode::ALPHA2);
isSame(
- 'Value "qq" is not a valid alpha-2 country code.',
+ 'Value "qq" is not a valid "alpha-2" country code.',
$rule->test('qq'),
);
$rule = $this->create(RespectCountryCode::ALPHA3);
isSame(
- 'Value "QQQ" is not a valid alpha-3 country code.',
+ 'Value "QQQ" is not a valid "alpha-3" country code.',
$rule->test('QQQ'),
);
$rule = $this->create(RespectCountryCode::NUMERIC);
isSame(
- 'Value "101010101" is not a valid numeric country code.',
+ 'Value "101010101" is not a valid "numeric" country code.',
$rule->test('101010101'),
);
}
@@ -64,7 +64,7 @@ public function testInvalidOption(): void
{
$rule = $this->create('qwerty');
isSame(
- 'Unknown country set: "qwerty". Available options: [alpha-2, alpha-3, numeric]',
+ 'Unknown country set: "qwerty". Available options: ["alpha-2", "alpha-3", "numeric"]',
$rule->test('US'),
);
}
diff --git a/tests/Rules/Cell/IsDirExistsTest.php b/tests/Rules/Cell/IsDirExistsTest.php
new file mode 100644
index 00000000..1a0b3686
--- /dev/null
+++ b/tests/Rules/Cell/IsDirExistsTest.php
@@ -0,0 +1,47 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test(__DIR__));
+ isSame('', $rule->test(__DIR__ . '/'));
+ isSame('', $rule->test(__DIR__ . '/../'));
+ isSame('', $rule->test(__DIR__ . '/../../'));
+ isSame('', $rule->test(PROJECT_ROOT));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'Directory "qwerty" not found',
+ $rule->test('qwerty'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/IsDomainTest.php b/tests/Rules/Cell/IsDomainTest.php
index ed46a552..04e2e0ac 100644
--- a/tests/Rules/Cell/IsDomainTest.php
+++ b/tests/Rules/Cell/IsDomainTest.php
@@ -34,7 +34,6 @@ public function testPositive(): void
isSame('', $rule->test('sub.sub.example.com'));
isSame('', $rule->test('sub.sub-example.com'));
isSame('', $rule->test('sub-sub-example.com'));
- isSame('', $rule->test('sub-sub-example.qwerty'));
$rule = $this->create(false);
isSame(null, $rule->validate('example'));
@@ -47,5 +46,9 @@ public function testNegative(): void
'Value "example" is not a valid domain',
$rule->test('example'),
);
+ isSame(
+ 'Value "sub-sub-example.qwerty" is not a valid domain',
+ $rule->test('sub-sub-example.qwerty'),
+ );
}
}
diff --git a/tests/Rules/Cell/IsFileExistsTest.php b/tests/Rules/Cell/IsFileExistsTest.php
new file mode 100644
index 00000000..ce9406dd
--- /dev/null
+++ b/tests/Rules/Cell/IsFileExistsTest.php
@@ -0,0 +1,45 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test(__FILE__));
+ isSame('', $rule->test('README.md'));
+ isSame('', $rule->test('./README.md'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'File "qwerty" not found',
+ $rule->test('qwerty'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/IsIpPrivateTest.php b/tests/Rules/Cell/IsIpPrivateTest.php
new file mode 100644
index 00000000..b123a20d
--- /dev/null
+++ b/tests/Rules/Cell/IsIpPrivateTest.php
@@ -0,0 +1,49 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test('10.0.0.1'));
+ isSame('', $rule->test('fc01:0db8:85a3:08d3:1319:8a2e:0370:7334'));
+ isSame('', $rule->test('fd01:0db8:85a3:08d3:1319:8a2e:0370:7334'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'Value "189.0.0.1" is not a private IP address.',
+ $rule->test('189.0.0.1'),
+ );
+ isSame(
+ 'Value "2020:0000:0000:0000:0000:0000:0000:0001" is not a private IP address.',
+ $rule->test('2020:0000:0000:0000:0000:0000:0000:0001'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/IsIpReservedTest.php b/tests/Rules/Cell/IsIpReservedTest.php
new file mode 100644
index 00000000..b153ecf4
--- /dev/null
+++ b/tests/Rules/Cell/IsIpReservedTest.php
@@ -0,0 +1,52 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test('0.0.0.0'));
+ isSame('', $rule->test('127.0.0.1'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'Value "45.46.47.48" is not a reserved IP address.',
+ $rule->test('45.46.47.48'),
+ );
+ isSame(
+ 'Value "fd00:0000:0000:0000:0000:0000:0000:0001" is not a reserved IP address.',
+ $rule->test('fd00:0000:0000:0000:0000:0000:0000:0001'),
+ );
+ isSame(
+ 'Value "fc00:0000:0000:0000:0000:0000:0000:0001" is not a reserved IP address.',
+ $rule->test('fc00:0000:0000:0000:0000:0000:0000:0001'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/IsIpTest.php b/tests/Rules/Cell/IsIpTest.php
index cd8598dd..191dd0f3 100644
--- a/tests/Rules/Cell/IsIpTest.php
+++ b/tests/Rules/Cell/IsIpTest.php
@@ -16,14 +16,14 @@
namespace JBZoo\PHPUnit\Rules\Cell;
-use JBZoo\CsvBlueprint\Rules\Cell\IsIp4;
+use JBZoo\CsvBlueprint\Rules\Cell\IsIp;
use JBZoo\PHPUnit\Rules\TestAbstractCellRule;
use function JBZoo\PHPUnit\isSame;
final class IsIpTest extends TestAbstractCellRule
{
- protected string $ruleClass = IsIp4::class;
+ protected string $ruleClass = IsIp::class;
public function testPositive(): void
{
@@ -31,13 +31,14 @@ public function testPositive(): void
isSame(null, $rule->validate(''));
isSame('', $rule->test('127.0.0.1'));
isSame('', $rule->test('0.0.0.0'));
+ isSame('', $rule->test('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'));
}
public function testNegative(): void
{
$rule = $this->create(true);
isSame(
- 'Value "1.2.3" is not a valid IP',
+ 'Value "1.2.3" is not a valid IPv6 or IPv4',
$rule->test('1.2.3'),
);
}
diff --git a/tests/Rules/Cell/IsIpV4Test.php b/tests/Rules/Cell/IsIpV4Test.php
new file mode 100644
index 00000000..c60c90b3
--- /dev/null
+++ b/tests/Rules/Cell/IsIpV4Test.php
@@ -0,0 +1,48 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test('127.0.0.1'));
+ isSame('', $rule->test('0.0.0.0'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'Value "1.2.3" is not a valid IPv4',
+ $rule->test('1.2.3'),
+ );
+ isSame(
+ 'Value "2001:0db8:85a3:08d3:1319:8a2e:0370:7334" is not a valid IPv4',
+ $rule->test('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/IsIpV6Test.php b/tests/Rules/Cell/IsIpV6Test.php
new file mode 100644
index 00000000..312452e0
--- /dev/null
+++ b/tests/Rules/Cell/IsIpV6Test.php
@@ -0,0 +1,47 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'Value "1.2.3" is not a valid IPv6',
+ $rule->test('1.2.3'),
+ );
+ isSame(
+ 'Value "127.0.0.1" is not a valid IPv6',
+ $rule->test('127.0.0.1'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/LanguageCodeTest.php b/tests/Rules/Cell/IsLanguageCodeTest.php
similarity index 82%
rename from tests/Rules/Cell/LanguageCodeTest.php
rename to tests/Rules/Cell/IsLanguageCodeTest.php
index 0578e152..44a43dac 100644
--- a/tests/Rules/Cell/LanguageCodeTest.php
+++ b/tests/Rules/Cell/IsLanguageCodeTest.php
@@ -16,14 +16,14 @@
namespace JBZoo\PHPUnit\Rules\Cell;
-use JBZoo\CsvBlueprint\Rules\Cell\LanguageCode;
+use JBZoo\CsvBlueprint\Rules\Cell\IsLanguageCode;
use JBZoo\PHPUnit\Rules\TestAbstractCellRule;
use function JBZoo\PHPUnit\isSame;
-final class LanguageCodeTest extends TestAbstractCellRule
+final class IsLanguageCodeTest extends TestAbstractCellRule
{
- protected string $ruleClass = LanguageCode::class;
+ protected string $ruleClass = IsLanguageCode::class;
public function testPositive(): void
{
@@ -41,7 +41,7 @@ public function testNegative(): void
{
$rule = $this->create('alpha-2');
isSame(
- 'Value "qq" is not a valid alpha-2 language code.',
+ 'Value "qq" is not a valid "alpha-2" language code.',
$rule->test('qq'),
);
}
@@ -50,7 +50,7 @@ public function testInvalidOption(): void
{
$rule = $this->create('qwerty');
isSame(
- 'Unknown language set: "qwerty". Available options: [alpha-2, alpha-3]',
+ 'Unknown language set: "qwerty". Available options: ["alpha-2", "alpha-3"]',
$rule->test('US'),
);
}
diff --git a/tests/Rules/Cell/IsMacAddressTest.php b/tests/Rules/Cell/IsMacAddressTest.php
new file mode 100644
index 00000000..09d05345
--- /dev/null
+++ b/tests/Rules/Cell/IsMacAddressTest.php
@@ -0,0 +1,44 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test('00:11:22:33:44:55'));
+ isSame('', $rule->test('af-AA-22-33-44-55'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'Value "127.0.0.1" is not a valid MAC address.',
+ $rule->test('127.0.0.1'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/IsPublicDomainSuffixTest.php b/tests/Rules/Cell/IsPublicDomainSuffixTest.php
new file mode 100644
index 00000000..5621f5d5
--- /dev/null
+++ b/tests/Rules/Cell/IsPublicDomainSuffixTest.php
@@ -0,0 +1,48 @@
+create(true);
+ isSame(null, $rule->validate(''));
+ isSame('', $rule->test('com'));
+ isSame('', $rule->test('CO.UK'));
+ }
+
+ public function testNegative(): void
+ {
+ $rule = $this->create(true);
+ isSame(
+ 'The value "127.0.0.1" is not a valid public domain suffix. Example: "com", "nom.br", "net" etc.',
+ $rule->test('127.0.0.1'),
+ );
+ isSame(
+ 'The value "invalid.com" is not a valid public domain suffix. Example: "com", "nom.br", "net" etc.',
+ $rule->test('invalid.com'),
+ );
+ }
+}
diff --git a/tests/Rules/Cell/NotAllowValuesTest.php b/tests/Rules/Cell/NotAllowValuesTest.php
index abbcb715..39b4bfe3 100644
--- a/tests/Rules/Cell/NotAllowValuesTest.php
+++ b/tests/Rules/Cell/NotAllowValuesTest.php
@@ -52,7 +52,8 @@ public function testInvalidOption(): void
$rule = $this->create('qwe');
isSame(
'"not_allow_values" at line 1, column "prop". ' .
- 'Unexpected error: Invalid option "qwe" for the "not_allow_values" rule. It should be array of strings.',
+ 'Unexpected error: Invalid option "qwe" for the "not_allow_values" rule. ' .
+ 'It should be array of strings.',
(string)$rule->validate('true'),
);
}
diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php
index 64909137..20426e68 100644
--- a/tests/SchemaTest.php
+++ b/tests/SchemaTest.php
@@ -302,6 +302,8 @@ public function testMatchTypes(): void
'float !== null',
'int !== null',
'null !== string',
+ 'float !== int',
+ 'int !== string',
];
$invalidPairs = [];
diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php
index b5338a69..971c3080 100644
--- a/tests/UtilsTest.php
+++ b/tests/UtilsTest.php
@@ -103,6 +103,18 @@ public function testFindFilesNotFound(): void
isSame([], $this->getFileName(Utils::findFiles(['demo.csv'])));
}
+ public function testPrintList(): void
+ {
+ isSame('["one", "two", "three"]', Utils::printList(['one', 'two', 'three']));
+ isSame('["one", "two", "three"]', Utils::printList(['one', 'two', 'three'], 'c'));
+ isSame('"one"', Utils::printList(['one']));
+ isSame('"one"', Utils::printList('one'));
+ isSame('"one"', Utils::printList(['one'], 'c'));
+ isSame('"one"', Utils::printList('one', 'c'));
+ isSame('[]', Utils::printList([]));
+ isSame('[]', Utils::printList([], 'c'));
+ }
+
public function testColorsTags(): void
{
$packs = [
diff --git a/tests/schemas/todo.yml b/tests/schemas/todo.yml
index 433a1636..74dd06a5 100644
--- a/tests/schemas/todo.yml
+++ b/tests/schemas/todo.yml
@@ -41,6 +41,7 @@ columns:
rules:
# https://github.com/Respect/Validation/blob/main/docs/08-list-of-rules-by-category.md
is_bool_value: true # https://github.com/Respect/Validation/blob/main/docs/rules/BoolVal.md
+ is_null: true # see empty_values
# Dates
age: 35
@@ -61,8 +62,8 @@ columns:
# ids
is_credit_card: brands[] # https://github.com/Respect/Validation/blob/main/docs/rules/CreditCard.md
- is_iban: true
- postal_code: country code # https://github.com/Respect/Validation/blob/main/docs/rules/PostalCode.md
+ is_postal_code: country code # https://github.com/Respect/Validation/blob/main/docs/rules/PostalCode.md
+ is_phone: true
is_bsn: true
is_cnh: true
is_cnpj: true
@@ -79,6 +80,7 @@ columns:
is_polish_id_card: true
is_portuguese_nif: true
is_bic: true
+ is_iban: true
is_card_number: true
# Hashes
@@ -161,18 +163,6 @@ columns:
is_hmac_sha256: true
is_hmac_sha512: true
- # Web
- ip: 220.78.168.0/21 # https://github.com/Respect/Validation/blob/main/docs/rules/Ip.md
- is_ip6: true # Check if the value is a valid IPv6 address. Example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
- ip4_subnets: [ 192.168.1.2/24 ] # If not set, then no subnet check
- is_mac_address: true
- is_public_domain_suffix: true
- # is_email: true # improve
- is_tid: true
- # is_domain: true # improve
- is_video_url: true
- is_null: true # see empty_values
-
# URL
url_scheme: https # Can be set of schemes [http, https, ftp]
url_host: example.com # Can be regex
@@ -190,8 +180,6 @@ columns:
# Math
is_fibonacci: true
is_prime_number: true
- is_digit: true
- is_digits: true
is_even: true
is_odd: true
is_roman: true
@@ -199,13 +187,14 @@ columns:
# Strings
is_alnum: true
+ is_hex: true
+ is_binary: true
is_alpha: true
is_charset: true
is_hex_rgb_color: true
no_whitespace: true
- is_phone: true
+
is_punct: true
- is_spaces: true
is_space: true
is_regex: true
is_version: true