diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index 305e6b75..8c4b63d1 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -13,6 +13,8 @@ name: Demo on: + release: + types: [ created ] workflow_run: workflows: [ "Publish Docker" ] types: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c4b5aea0..0cc6b61e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -104,7 +104,11 @@ jobs: tools: composer extensions: ast - - name: Build project + - name: Install project + run: make build --no-print-directory + + ## To see the difference between the current and the lowest versions + - name: Downgrade dependencies run: make update --no-print-directory - name: 🧪 PHPUnit Tests @@ -143,7 +147,11 @@ jobs: tools: composer extensions: ast - - name: Build project + - name: Install project + run: make build --no-print-directory + + ## To see the difference between the current and the latest versions + - name: Upgrade dependencies run: make update --no-print-directory - name: 🧪 PHPUnit Tests @@ -277,6 +285,7 @@ jobs: context: . push: true tags: jbzoo/csv-blueprint:master + platforms: linux/amd64,linux/arm64/v8,linux/386 verify-ga: diff --git a/Makefile b/Makefile index bd88cb44..49844ff9 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ INVALID_SCHEMA ?= --schema='./tests/schemas/demo_invalid.yml' # Build/install ######################################################################################################## build: ##@Project Build project in development mode - @composer install --optimize-autoloader + @composer install --optimize-autoloader --ansi @rm -f `pwd`/ci-report-converter @make build-version @@ -56,7 +56,7 @@ build-version: ##@Project Save version info update: ##@Project Update dependencies @echo "Composer flags: $(JBZOO_COMPOSER_UPDATE_FLAGS)" - @composer update $(JBZOO_COMPOSER_UPDATE_FLAGS) + @composer update $(JBZOO_COMPOSER_UPDATE_FLAGS) --ansi # Demo ################################################################################################################# diff --git a/README.md b/README.md index 20ae88d3..7a546e2e 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-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-142/54/8-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml) +[![Static Badge](https://img.shields.io/badge/Rules-304-green?label=Total%20number%20of%20rules&labelColor=darkgreen&color=gray)](schema-examples/full.yml) [![Static Badge](https://img.shields.io/badge/Rules-93-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-119/54/8-green?label=Plan%20to%20add&labelColor=gray&color=gray)](tests/schemas/todo.yml) ## Introduction @@ -282,9 +282,28 @@ columns: # See: https://en.wikipedia.org/wiki/ISO_639. is_language_code: alpha-2 # Examples: "en", "eng" + # Filesystem (with IO!) 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!). + # Mathematical + is_fibonacci: true # Validates whether the input follows the Fibonacci integer sequence. Example: "8", "13". + is_prime_number: true # Validates a prime number. Example: "3", "5", "7", "11". + is_even: true # Check if the value is an even number. Example: "2", "4", "6". + is_odd: true # Check if the value is an odd number. Example: "1", "7", "11". + is_roman: true # Validates if the input is a Roman numeral. Example: "I", "IV", "XX". + + # Identifications + phone: ALL # Validates if the input is a phone number. Specify the country code to validate the phone number for a specific country. Example: "ALL", "US", "BR".". + + # Misc + is_version: true # Validates the string as version numbers using Semantic Versioning. Example: "1.2.3". + is_punct: true # Validates whether the input composed by only punctuation characters. Example: "!@#$%^&*()". + is_vowel: true # Validates whether the input contains only vowels. Example: "aei". + is_consonant: true # Validates if the input contains only consonants. Example: "bcd". + is_alnum: true # Validates whether the input is only alphanumeric. Example: "aBc123". + is_alpha: true # This is similar to `is_alnum`, but it does not allow numbers. Example: "aBc". + #################################################################################################################### # Data validation for the entire(!) column using different data aggregation methods. # Depending on the file size and the chosen aggregation method - this can use a lot of RAM time. diff --git a/composer.json b/composer.json index f905c783..55d7ea67 100644 --- a/composer.json +++ b/composer.json @@ -27,20 +27,23 @@ "prefer-stable" : true, "require" : { - "php" : "^8.1", - "ext-mbstring" : "*", + "php" : "^8.1", + "ext-mbstring" : "*", - "league/csv" : "^9.15.0", - "jbzoo/data" : "^7.1.1", - "jbzoo/cli" : "^7.1.8", - "jbzoo/utils" : "^7.2.0", - "jbzoo/ci-report-converter" : "^7.2.1", + "league/csv" : "^9.15.0", + "jbzoo/data" : "^7.1.1", + "jbzoo/cli" : "^7.1.8", + "jbzoo/utils" : "^7.2.0", + "jbzoo/ci-report-converter" : "^7.2.1", - "symfony/yaml" : ">=6.4.3", - "symfony/filesystem" : ">=6.4.3", - "symfony/finder" : ">=6.4.0", - "markrogoyski/math-php" : "^2.9.0", - "respect/validation" : "^2.3.5" + "symfony/yaml" : ">=6.4.3", + "symfony/filesystem" : ">=6.4.3", + "symfony/finder" : ">=6.4.0", + "markrogoyski/math-php" : "^2.9.0", + "respect/validation" : "^2.3.6", + "giggsey/libphonenumber-for-php-lite" : "^8.13.33", + "giggsey/locale" : "^2.5", + "symfony/polyfill-mbstring" : "^1.29.0" }, "require-dev" : { diff --git a/composer.lock b/composer.lock index 8541c069..da9a52c9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4319f3136d58ff1f5669b4fba8e4bdb0", + "content-hash": "1adc3bef17fcdbac873f8ab7b4d6a5ff", "packages": [ { "name": "bluepsyduck/symfony-process-manager", @@ -63,6 +63,142 @@ }, "time": "2021-12-03T21:30:28+00:00" }, + { + "name": "giggsey/libphonenumber-for-php-lite", + "version": "8.13.33", + "source": { + "type": "git", + "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git", + "reference": "9e2489ea908a9cc3826218674880ac7a1bfe95aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/9e2489ea908a9cc3826218674880ac7a1bfe95aa", + "reference": "9e2489ea908a9cc3826218674880ac7a1bfe95aa", + "shasum": "" + }, + "require": { + "php": "^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "conflict": { + "giggsey/libphonenumber-for-php": "*" + }, + "require-dev": { + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^3.12", + "infection/infection": "^0.26.16", + "pear/pear-core-minimal": "^1.10.11", + "pear/pear_exception": "^1.0.2", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.17.4", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.2", + "phpunit/phpunit": "^9.5.26", + "symfony/console": "^6.0" + }, + "suggest": { + "giggsey/libphonenumber-for-php": "Use libphonenumber-for-php for geocoding, carriers, timezones and matching" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "libphonenumber\\": "src/" + }, + "exclude-from-classmap": [ + "/src/data/", + "/src/carrier/data/", + "/src/geocoding/data/", + "/src/timezone/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "A lite version of giggsey/libphonenumber-for-php, which is a PHP Port of Google's libphonenumber", + "homepage": "https://github.com/giggsey/libphonenumber-for-php-lite", + "keywords": [ + "geocoding", + "geolocation", + "libphonenumber", + "mobile", + "phonenumber", + "validation" + ], + "support": { + "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues", + "source": "https://github.com/giggsey/libphonenumber-for-php-lite" + }, + "time": "2024-03-25T07:35:47+00:00" + }, + { + "name": "giggsey/locale", + "version": "2.5", + "source": { + "type": "git", + "url": "https://github.com/giggsey/Locale.git", + "reference": "e6d4540109a01dd2bc7334cdc842d6a6a67cf239" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/Locale/zipball/e6d4540109a01dd2bc7334cdc842d6a6a67cf239", + "reference": "e6d4540109a01dd2bc7334cdc842d6a6a67cf239", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "ext-json": "*", + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.5|^9.5", + "symfony/console": "^5.0|^6.0", + "symfony/filesystem": "^5.0|^6.0", + "symfony/finder": "^5.0|^6.0", + "symfony/process": "^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Giggsey\\Locale\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "Locale functions required by libphonenumber-for-php", + "support": { + "issues": "https://github.com/giggsey/Locale/issues", + "source": "https://github.com/giggsey/Locale/tree/2.5" + }, + "time": "2023-11-01T17:19:48+00:00" + }, { "name": "jbzoo/ci-report-converter", "version": "7.2.1", @@ -5395,12 +5531,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "430219a8462bb5c66c1a0c118239e9c3ab5179db" + "reference": "a2c33d8cc3719997b91b628eec3570d725cebf7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/430219a8462bb5c66c1a0c118239e9c3ab5179db", - "reference": "430219a8462bb5c66c1a0c118239e9c3ab5179db", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a2c33d8cc3719997b91b628eec3570d725cebf7e", + "reference": "a2c33d8cc3719997b91b628eec3570d725cebf7e", "shasum": "" }, "conflict": { @@ -5977,7 +6113,7 @@ "thinkcmf/thinkcmf": "<=5.1.7", "thorsten/phpmyfaq": "<3.2.2", "tikiwiki/tiki-manager": "<=17.1", - "tinymce/tinymce": "<5.10.9|>=6,<6.7.3", + "tinymce/tinymce": "<7", "tinymighty/wiki-seo": "<1.2.2", "titon/framework": "<9.9.99", "tobiasbg/tablepress": "<=2.0.0.0-RC1", @@ -6130,7 +6266,7 @@ "type": "tidelift" } ], - "time": "2024-03-26T20:04:27+00:00" + "time": "2024-03-26T22:04:37+00:00" }, { "name": "sabre/event", diff --git a/psalm.xml b/psalm.xml index 36c060aa..2fecab37 100644 --- a/psalm.xml +++ b/psalm.xml @@ -29,5 +29,10 @@ + + + + + diff --git a/schema-examples/full.json b/schema-examples/full.json index 2e74c900..85bb3043 100644 --- a/schema-examples/full.json +++ b/schema-examples/full.json @@ -111,8 +111,25 @@ "is_country_code" : "alpha-2", "is_language_code" : "alpha-2", + "is_file_exists" : true, - "is_dir_exists" : true + "is_dir_exists" : true, + + "is_dir_exists" : true, + "is_fibonacci" : true, + "is_prime_number" : true, + "is_even" : true, + "is_odd" : true, + "is_roman" : true, + + "phone" : "ALL", + + "is_version" : true, + "is_punct" : true, + "is_vowel" : true, + "is_consonant" : true, + "is_alnum" : true, + "is_alpha" : true }, "aggregate_rules" : { "is_unique" : true, diff --git a/schema-examples/full.php b/schema-examples/full.php index b9965e34..207cdd1a 100644 --- a/schema-examples/full.php +++ b/schema-examples/full.php @@ -135,6 +135,21 @@ 'is_file_exists' => true, 'is_dir_exists' => true, + + 'is_fibonacci' => true, + 'is_prime_number' => true, + 'is_even' => true, + 'is_odd' => true, + 'is_roman' => true, + + 'phone' => 'ALL', + + 'is_version' => true, + 'is_punct' => true, + 'is_vowel' => true, + 'is_consonant' => true, + 'is_alnum' => true, + 'is_alpha' => true, ], 'aggregate_rules' => [ diff --git a/schema-examples/full.yml b/schema-examples/full.yml index 5c98f9e3..ff98a445 100644 --- a/schema-examples/full.yml +++ b/schema-examples/full.yml @@ -196,9 +196,28 @@ columns: # See: https://en.wikipedia.org/wiki/ISO_639. is_language_code: alpha-2 # Examples: "en", "eng" + # Filesystem (with IO!) 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!). + # Mathematical + is_fibonacci: true # Validates whether the input follows the Fibonacci integer sequence. Example: "8", "13". + is_prime_number: true # Validates a prime number. Example: "3", "5", "7", "11". + is_even: true # Check if the value is an even number. Example: "2", "4", "6". + is_odd: true # Check if the value is an odd number. Example: "1", "7", "11". + is_roman: true # Validates if the input is a Roman numeral. Example: "I", "IV", "XX". + + # Identifications + phone: ALL # Validates if the input is a phone number. Specify the country code to validate the phone number for a specific country. Example: "ALL", "US", "BR".". + + # Misc + is_version: true # Validates the string as version numbers using Semantic Versioning. Example: "1.2.3". + is_punct: true # Validates whether the input composed by only punctuation characters. Example: "!@#$%^&*()". + is_vowel: true # Validates whether the input contains only vowels. Example: "aei". + is_consonant: true # Validates if the input contains only consonants. Example: "bcd". + is_alnum: true # Validates whether the input is only alphanumeric. Example: "aBc123". + is_alpha: true # This is similar to `is_alnum`, but it does not allow numbers. Example: "aBc". + #################################################################################################################### # Data validation for the entire(!) column using different data aggregation methods. # Depending on the file size and the chosen aggregation method - this can use a lot of RAM time. diff --git a/schema-examples/full_clean.yml b/schema-examples/full_clean.yml index 042f1389..b16f71ec 100644 --- a/schema-examples/full_clean.yml +++ b/schema-examples/full_clean.yml @@ -145,6 +145,21 @@ columns: is_file_exists: true is_dir_exists: true + is_fibonacci: true + is_prime_number: true + is_even: true + is_odd: true + is_roman: true + + phone: ALL + + is_version: true + is_punct: true + is_vowel: true + is_consonant: true + is_alnum: true + is_alpha: true + aggregate_rules: is_unique: true is_sorted: diff --git a/src/Rules/Cell/AbstractCellRule.php b/src/Rules/Cell/AbstractCellRule.php index 4adf1b4c..ee9c539e 100644 --- a/src/Rules/Cell/AbstractCellRule.php +++ b/src/Rules/Cell/AbstractCellRule.php @@ -18,6 +18,9 @@ use JBZoo\CsvBlueprint\Rules\AbstarctRule; +/** + * @SuppressWarnings(PHPMD.NumberOfChildren) + */ abstract class AbstractCellRule extends AbstarctRule { /** diff --git a/src/Rules/Cell/IsAlnum.php b/src/Rules/Cell/IsAlnum.php new file mode 100644 index 00000000..34caca39 --- /dev/null +++ b/src/Rules/Cell/IsAlnum.php @@ -0,0 +1,49 @@ + [ + 'true', + 'Validates whether the input is only alphanumeric. Example: "aBc123".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::alnum()->validate($cellValue)) { + return "The value \"{$cellValue}\" should contain only alphanumeric characters. " . + 'Example: "aBc123"'; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsAlpha.php b/src/Rules/Cell/IsAlpha.php new file mode 100644 index 00000000..e3c2fe98 --- /dev/null +++ b/src/Rules/Cell/IsAlpha.php @@ -0,0 +1,49 @@ + [ + 'true', + 'This is similar to `is_alnum`, but it does not allow numbers. Example: "aBc".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::alpha()->validate($cellValue)) { + return "The value \"{$cellValue}\" should contain only alphabetic characters. " . + 'Example: "aBc"'; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsConsonant.php b/src/Rules/Cell/IsConsonant.php new file mode 100644 index 00000000..2046434b --- /dev/null +++ b/src/Rules/Cell/IsConsonant.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Validates if the input contains only consonants. Example: "bcd".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::consonant()->validate($cellValue)) { + return "The value \"{$cellValue}\" should contain only consonants. Example: \"bcd\""; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsEven.php b/src/Rules/Cell/IsEven.php new file mode 100644 index 00000000..cc0db528 --- /dev/null +++ b/src/Rules/Cell/IsEven.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Check if the value is an even number. Example: "2", "4", "6".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::even()->validate($cellValue)) { + return "The value \"{$cellValue}\" should be an even number. Example: \"2\", \"4\", \"6\"."; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsFibonacci.php b/src/Rules/Cell/IsFibonacci.php new file mode 100644 index 00000000..b03573e3 --- /dev/null +++ b/src/Rules/Cell/IsFibonacci.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Validates whether the input follows the Fibonacci integer sequence. Example: "8", "13".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::fibonacci()->validate($cellValue)) { + return "The value \"{$cellValue}\" should be a Fibonacci integer. Example: \"8\", \"13\""; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsOdd.php b/src/Rules/Cell/IsOdd.php new file mode 100644 index 00000000..6a857ddd --- /dev/null +++ b/src/Rules/Cell/IsOdd.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Check if the value is an odd number. Example: "1", "7", "11".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::odd()->validate($cellValue)) { + return "The value \"{$cellValue}\" should be an odd number. Example: \"1\", \"7\", \"11\"."; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsPrimeNumber.php b/src/Rules/Cell/IsPrimeNumber.php new file mode 100644 index 00000000..97d52213 --- /dev/null +++ b/src/Rules/Cell/IsPrimeNumber.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Validates a prime number. Example: "3", "5", "7", "11".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::primeNumber()->validate($cellValue)) { + return "The value \"{$cellValue}\" should be a prime number. Example: \"3\", \"5\", \"7\", \"11\""; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsPunct.php b/src/Rules/Cell/IsPunct.php new file mode 100644 index 00000000..b4031072 --- /dev/null +++ b/src/Rules/Cell/IsPunct.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Validates whether the input composed by only punctuation characters. Example: "!@#$%^&*()".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::punct()->validate($cellValue)) { + return "The value \"{$cellValue}\" should be composed by only punctuation characters."; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsRoman.php b/src/Rules/Cell/IsRoman.php new file mode 100644 index 00000000..171b64a7 --- /dev/null +++ b/src/Rules/Cell/IsRoman.php @@ -0,0 +1,49 @@ + [ + 'true', + 'Validates if the input is a Roman numeral. Example: "I", "IV", "XX".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::roman()->validate($cellValue)) { + return "The value \"{$cellValue}\" should contain only Roman numeral. " . + 'Example: "I", "IV", "XX"'; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsVersion.php b/src/Rules/Cell/IsVersion.php new file mode 100644 index 00000000..1dd20cff --- /dev/null +++ b/src/Rules/Cell/IsVersion.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Validates the string as version numbers using Semantic Versioning. Example: "1.2.3".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::version()->validate($cellValue)) { + return "The value \"{$cellValue}\" should be a valid semantic version. Example: \"1.2.3\""; + } + + return null; + } +} diff --git a/src/Rules/Cell/IsVowel.php b/src/Rules/Cell/IsVowel.php new file mode 100644 index 00000000..f0a13cbf --- /dev/null +++ b/src/Rules/Cell/IsVowel.php @@ -0,0 +1,48 @@ + [ + 'true', + 'Validates whether the input contains only vowels. Example: "aei".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + if (!Validator::vowel()->validate($cellValue)) { + return "The value \"{$cellValue}\" should contain only vowels. Example: \"aei\""; + } + + return null; + } +} diff --git a/src/Rules/Cell/Phone.php b/src/Rules/Cell/Phone.php new file mode 100644 index 00000000..b39949a3 --- /dev/null +++ b/src/Rules/Cell/Phone.php @@ -0,0 +1,61 @@ + [ + 'ALL', + 'Validates if the input is a phone number. Specify the country code to validate the phone number ' . + 'for a specific country. Example: "ALL", "US", "BR".', + ], + ], + ]; + } + + public function validateRule(string $cellValue): ?string + { + if ($cellValue === '') { + return null; + } + + $countryCode = $this->getOptionAsString(); + if ($countryCode === '') { + return 'The country code is required. Example: "ALL", "US", "BR"'; + } + + if ($countryCode === 'ALL') { + $countryCode = null; + } + + // @phpstan-ignore-next-line + if (!Validator::phone($countryCode)->validate($cellValue)) { // @phan-suppress-current-line PhanParamTooMany + return $countryCode === null + ? "The value \"{$cellValue}\" has invalid phone number format." + : "The value \"{$cellValue}\" has invalid phone number format for country \"{$countryCode}\"."; + } + + return null; + } +} diff --git a/tests/Rules/Cell/IsAlnumTest.php b/tests/Rules/Cell/IsAlnumTest.php new file mode 100644 index 00000000..cc4f0d0b --- /dev/null +++ b/tests/Rules/Cell/IsAlnumTest.php @@ -0,0 +1,71 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + 'b', + 'c', + 'D', + 'df', + 'Bcdf', + 'fdbC', + 'aBc123', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + ';', + '!@#$%^&*()', + 'aBc 123', + 'aBc-123', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should contain only alphanumeric characters. Example: \"aBc123\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsAlphaTest.php b/tests/Rules/Cell/IsAlphaTest.php new file mode 100644 index 00000000..c593fe33 --- /dev/null +++ b/tests/Rules/Cell/IsAlphaTest.php @@ -0,0 +1,72 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + 'b', + 'c', + 'D', + 'df', + 'Bcdf', + 'fdbC', + 'aBc', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + ';', + '!@#$%^&*()', + 'aBc123', + 'aBc 123', + 'aBc-123', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should contain only alphabetic characters. Example: \"aBc\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsConsonantTest.php b/tests/Rules/Cell/IsConsonantTest.php new file mode 100644 index 00000000..872edbd1 --- /dev/null +++ b/tests/Rules/Cell/IsConsonantTest.php @@ -0,0 +1,70 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + 'b', + 'c', + 'D', + 'df', + 'Bcdf', + 'fdbC', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + '16', + 'a', + 'Foo', + 'basic', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should contain only consonants. Example: \"bcd\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsEvenTest.php b/tests/Rules/Cell/IsEvenTest.php new file mode 100644 index 00000000..710be3e2 --- /dev/null +++ b/tests/Rules/Cell/IsEvenTest.php @@ -0,0 +1,68 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + '0', + '2', + '6', + '34', + '-34', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + '5', + '101', + '-101', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should be an even number. Example: \"2\", \"4\", \"6\".", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsFibonacciTest.php b/tests/Rules/Cell/IsFibonacciTest.php new file mode 100644 index 00000000..6fd2bac7 --- /dev/null +++ b/tests/Rules/Cell/IsFibonacciTest.php @@ -0,0 +1,67 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + '1', + '2', + '3', + '34', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + '100', + '101', + '6', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should be a Fibonacci integer. Example: \"8\", \"13\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsOddTest.php b/tests/Rules/Cell/IsOddTest.php new file mode 100644 index 00000000..6b1e9c31 --- /dev/null +++ b/tests/Rules/Cell/IsOddTest.php @@ -0,0 +1,68 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + '1', + '3', + '7', + '35', + '-35', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + '4', + '100', + '-100', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should be an odd number. Example: \"1\", \"7\", \"11\".", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsPhoneTest.php b/tests/Rules/Cell/IsPhoneTest.php new file mode 100644 index 00000000..d73ed290 --- /dev/null +++ b/tests/Rules/Cell/IsPhoneTest.php @@ -0,0 +1,78 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + 'III', + 'IV', + 'VI', + 'XIX', + 'XLII', + 'LXII', + 'CXLIX', + 'CLIII', + 'MCCXXXIV', + 'MMXXIV', + 'MCMLXXV', + 'MMMMCMXCIX', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + ' ', + 'IIII', + 'IVVVX', + 'CCDC', + 'MXM', + 'XIIIIIIII', + 'MIMIMI', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should contain only Roman numeral. Example: \"I\", \"IV\", \"XX\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsPrimeNumberTest.php b/tests/Rules/Cell/IsPrimeNumberTest.php new file mode 100644 index 00000000..fbc7821f --- /dev/null +++ b/tests/Rules/Cell/IsPrimeNumberTest.php @@ -0,0 +1,68 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + '2', + '3', + '7', + '11', + '101', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + '100', + '114', + '6', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should be a prime number. Example: \"3\", \"5\", \"7\", \"11\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsPunctTest.php b/tests/Rules/Cell/IsPunctTest.php new file mode 100644 index 00000000..615c3a97 --- /dev/null +++ b/tests/Rules/Cell/IsPunctTest.php @@ -0,0 +1,73 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + '.', + ',;:', + '-@#$*', + '()[]{}', + '!@#$%^&*(){}', + "[]?+=/\\-_|\"',><.", + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + '16-50', + 'a', + ' ', + 'Foo', + '12.1', + '-12', + '( )_{}', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should be composed by only punctuation characters.", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsRomanTest.php b/tests/Rules/Cell/IsRomanTest.php new file mode 100644 index 00000000..62206f67 --- /dev/null +++ b/tests/Rules/Cell/IsRomanTest.php @@ -0,0 +1,79 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + 'III', + 'IV', + 'VI', + 'XIX', + 'XLII', + 'LXII', + 'CXLIX', + 'CLIII', + 'MCCXXXIV', + 'MMXXIV', + 'MCMLXXV', + 'MMMMCMXCIX', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + ' ', + 'IIII', + 'IVVVX', + 'CCDC', + 'MXM', + 'XIIIIIIII', + 'MIMIMI', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should contain only Roman numeral. Example: \"I\", \"IV\", \"XX\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsVersionTest.php b/tests/Rules/Cell/IsVersionTest.php new file mode 100644 index 00000000..9bae0bb3 --- /dev/null +++ b/tests/Rules/Cell/IsVersionTest.php @@ -0,0 +1,65 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + '1.0.0', + '1.0.0-alpha', + '1.0.0-alpha.1', + '1.0.0-0.3.7', + '1.0.0-x.7.z.92', + '1.3.7+build.2.b8f12d7', + '1.3.7-rc.1', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = ['1', '1.3.7++', '1.3.7--', \uniqid('', true), '1.2.3.4', '1.2.3.4-beta', 'beta']; + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should be a valid semantic version. Example: \"1.2.3\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/IsVowelTest.php b/tests/Rules/Cell/IsVowelTest.php new file mode 100644 index 00000000..a11e6387 --- /dev/null +++ b/tests/Rules/Cell/IsVowelTest.php @@ -0,0 +1,76 @@ +create(true); + isSame(null, $rule->validate('')); + + $valid = [ + '', + 'a', + 'e', + 'i', + 'o', + 'u', + 'aeiou', + 'uoiea', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + $rule = $this->create(false); + isSame(null, $rule->validate(' 1')); + } + + public function testNegative(): void + { + $rule = $this->create(true); + + $invalid = [ + ' ', + "\n", + "\t", + "\r", + '16', + 'F', + 'g', + 'Foo', + 'basic', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" should contain only vowels. Example: \"aei\"", + $rule->test($value), + $value, + ); + } + } +} diff --git a/tests/Rules/Cell/PhoneTest.php b/tests/Rules/Cell/PhoneTest.php new file mode 100644 index 00000000..5985854e --- /dev/null +++ b/tests/Rules/Cell/PhoneTest.php @@ -0,0 +1,104 @@ +create('ALL'); + isSame(null, $rule->validate('')); + + $valid = [ + '', + '+1 650 253 00 00', + '+7 (999) 999-99-99', + '+7(999)999-99-99', + '+7(999)999-9999', + '+33(1)22 22 22 22', + '+1 650 253 00 00', + '+7 (999) 999-99-99', + '+7(999)999-99-99', + '+7(999)999-9999', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + } + + public function testNegative(): void + { + $rule = $this->create('ALL'); + + $invalid = [ + 'qwerty', + 'qa234qwerty', + ]; + + foreach ($invalid as $value) { + isSame( + "The value \"{$value}\" has invalid phone number format.", + $rule->test($value), + $value, + ); + } + } + + public function testByCountryCode(): void + { + $rule = $this->create('BR'); + $valid = [ + '+55 11 91111 1111', + '11 91111 1111', + '+5511911111111', + '11911111111', + '11 91111 1111', + ]; + + foreach ($valid as $value) { + isSame('', $rule->test($value), $value); + } + + isSame( + 'The value "+7(999)999-9999" has invalid phone number format for country "BR".', + $rule->test('+7(999)999-9999'), + ); + } + + public function testInvalidCountryCode(): void + { + $rule = $this->create('QWERTY'); + isSame( + '"phone" at line 1, column "prop". Unexpected error: Invalid country code QWERTY.', + (string)$rule->validate('+1 650 253 00 00'), + ); + + $rule = $this->create(''); + isSame( + '"phone" at line 1, column "prop". The country code is required. Example: "ALL", "US", "BR".', + (string)$rule->validate('+1 650 253 00 00'), + ); + } +} diff --git a/tests/schemas/todo.yml b/tests/schemas/todo.yml index 74dd06a5..29ec3131 100644 --- a/tests/schemas/todo.yml +++ b/tests/schemas/todo.yml @@ -48,22 +48,12 @@ columns: dateperiod: 1 dateinterval: 1 - # FS - is_dir_name: true - is_dir_name_full: true - is_file_name: true - is_file_name_full: true - is_file_size: true - is_file_extension: true - is_mimetype: true - # Codes subdivision_code: [ ] # https://github.com/Respect/Validation/blob/main/docs/rules/SubdivisionCode.md # ids is_credit_card: brands[] # https://github.com/Respect/Validation/blob/main/docs/rules/CreditCard.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 @@ -177,30 +167,15 @@ columns: email_domain: example.com # Can be regex email_user: user # Can be regex - # Math - is_fibonacci: true - is_prime_number: true - is_even: true - is_odd: true - is_roman: true - is_perfect_square: true - # 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_punct: true - is_space: true - is_regex: true - is_version: true - is_vowel: true - is_consonant: true - is_xdigit: true + # is_space: true + # is_xdigit: true custom_func: callbak function