diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6cad770..75d392c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,4 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
For a full diff see [`dc5f5d1...main`][dc5f5d1...main].
+### Added
+
+- Added `Composer\TildeVersionRange` as a value object ([#2]), by [@localheinz]
+
[dc5f5d1...main]: https://github.com/ergebnis/version-constraint/compare/dc5f5d1...main
+
+[#1]: https://github.com/ergebnis/version-constraint/pull/2
+
+[@localheinz]: https://github.com/localheinz
diff --git a/README.md b/README.md
index 269585e..9da8b83 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,25 @@ composer require ergebnis/version-constraint
## Usage
-💡 This is a great place for showing a few usage examples!
+This project comes with the following components:
+
+- [`Ergebnis\VersionConstraint\Composer\TildeVersionRange`](#composertildeversionrange)
+
+### `Composer\TildeVersionRange`
+
+#### Create a `Composer\TildeVersionRange` from a string
+
+```php
+toString(); // ~1.0.0
+```
## Changelog
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index eb79571..ac18d10 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -1,2 +1,51 @@
-
+
+
+
+ valid
+
+
+
+
+ valid
+
+
+
+
+ valid
+
+
+
+
+ valid
+
+
+
+
+ valid
+
+
+
+
+ valid
+
+
+
+
+ $otherValue
+ $otherValue
+
+
+ $otherValue
+ $otherValue
+
+
+ valid
+
+
+
+
+ valid
+
+
+
diff --git a/src/Example.php b/src/Composer/Exception/InvalidTildeVersionRange.php
similarity index 57%
rename from src/Example.php
rename to src/Composer/Exception/InvalidTildeVersionRange.php
index 1af494d..5c41d1b 100644
--- a/src/Example.php
+++ b/src/Composer/Exception/InvalidTildeVersionRange.php
@@ -11,21 +11,15 @@
* @see https://github.com/ergebnis/version-constraint
*/
-namespace Ergebnis\VersionConstraint;
+namespace Ergebnis\VersionConstraint\Composer\Exception;
-final class Example
+final class InvalidTildeVersionRange extends \InvalidArgumentException
{
- private function __construct(private readonly string $value)
- {
- }
-
public static function fromString(string $value): self
{
- return new self($value);
- }
-
- public function toString(): string
- {
- return $this->value;
+ return new self(\sprintf(
+ 'Value "%s" does not appear to be a valid value.',
+ $value,
+ ));
}
}
diff --git a/src/Composer/TildeVersionRange.php b/src/Composer/TildeVersionRange.php
new file mode 100644
index 0000000..9459489
--- /dev/null
+++ b/src/Composer/TildeVersionRange.php
@@ -0,0 +1,40 @@
+v)?(?P0|[1-9]\d*)(\.(?P0|[1-9]\d*)(\.(?P0|[1-9]\d*)(\.(?P0|[1-9]\d*))?)?)?(-(?P(alpha|beta|dev|RC|stable)))?$/';
+
+ private function __construct(private readonly string $value)
+ {
+ }
+
+ /**
+ * @throws Exception\InvalidTildeVersionRange
+ */
+ public static function fromString(string $value): self
+ {
+ if (1 !== \preg_match(self::REGEX, $value)) {
+ throw Exception\InvalidTildeVersionRange::fromString($value);
+ }
+
+ return new self($value);
+ }
+
+ public function toString(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/test/DataProvider/CaretVersionRangeProvider.php b/test/DataProvider/CaretVersionRangeProvider.php
new file mode 100644
index 0000000..23b3dac
--- /dev/null
+++ b/test/DataProvider/CaretVersionRangeProvider.php
@@ -0,0 +1,40 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $values = [
+ '^1',
+ '^1.0',
+ '^1.0.0',
+ '^v1',
+ '^v1.0',
+ '^v1.0.0',
+ ];
+
+ foreach ($values as $value) {
+ yield $value => [
+ $value,
+ ];
+ }
+ }
+}
diff --git a/test/DataProvider/GreaterThanOrEqualVersionRangeProvider.php b/test/DataProvider/GreaterThanOrEqualVersionRangeProvider.php
new file mode 100644
index 0000000..f69a79b
--- /dev/null
+++ b/test/DataProvider/GreaterThanOrEqualVersionRangeProvider.php
@@ -0,0 +1,40 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $values = [
+ '>=1',
+ '>=1.0',
+ '>=1.0.0',
+ '>=v1',
+ '>=v1.0',
+ '>=v1.0.0',
+ ];
+
+ foreach ($values as $value) {
+ yield $value => [
+ $value,
+ ];
+ }
+ }
+}
diff --git a/test/DataProvider/GreaterThanVersionRangeProvider.php b/test/DataProvider/GreaterThanVersionRangeProvider.php
new file mode 100644
index 0000000..795dc11
--- /dev/null
+++ b/test/DataProvider/GreaterThanVersionRangeProvider.php
@@ -0,0 +1,40 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $values = [
+ '>1',
+ '>1.0',
+ '>1.0.0',
+ '>v1',
+ '>v1.0',
+ '>v1.0.0',
+ ];
+
+ foreach ($values as $value) {
+ yield $value => [
+ $value,
+ ];
+ }
+ }
+}
diff --git a/test/DataProvider/HyphenatedVersionRangeProvider.php b/test/DataProvider/HyphenatedVersionRangeProvider.php
new file mode 100644
index 0000000..e41864b
--- /dev/null
+++ b/test/DataProvider/HyphenatedVersionRangeProvider.php
@@ -0,0 +1,57 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $leftValues = [
+ '1',
+ '1.0',
+ '1.0.0',
+ 'v1',
+ 'v1.0',
+ 'v1.0.0',
+ ];
+
+ $rightValues = [
+ '2',
+ '2.0',
+ '2.0.0',
+ 'v2',
+ 'v2.0',
+ 'v2.0.0',
+ ];
+
+ foreach ($leftValues as $leftValue) {
+ foreach ($rightValues as $rightValue) {
+ $value = \sprintf(
+ '%s - %s',
+ $leftValue,
+ $rightValue,
+ );
+
+ yield $value => [
+ $value,
+ ];
+ }
+ }
+ }
+}
diff --git a/test/DataProvider/LessThanOrEqualVersionRangeProvider.php b/test/DataProvider/LessThanOrEqualVersionRangeProvider.php
new file mode 100644
index 0000000..5a43203
--- /dev/null
+++ b/test/DataProvider/LessThanOrEqualVersionRangeProvider.php
@@ -0,0 +1,40 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $values = [
+ '<=1',
+ '<=1.0',
+ '<=1.0.0',
+ '<=v1',
+ '<=v1.0',
+ '<=v1.0.0',
+ ];
+
+ foreach ($values as $value) {
+ yield $value => [
+ $value,
+ ];
+ }
+ }
+}
diff --git a/test/DataProvider/LessThanVersionRangeProvider.php b/test/DataProvider/LessThanVersionRangeProvider.php
new file mode 100644
index 0000000..a4ca204
--- /dev/null
+++ b/test/DataProvider/LessThanVersionRangeProvider.php
@@ -0,0 +1,40 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $values = [
+ '<1',
+ '<1.0',
+ '<1.0.0',
+ ' [
+ $value,
+ ];
+ }
+ }
+}
diff --git a/test/DataProvider/TildeVersionRangeProvider.php b/test/DataProvider/TildeVersionRangeProvider.php
new file mode 100644
index 0000000..dafe33b
--- /dev/null
+++ b/test/DataProvider/TildeVersionRangeProvider.php
@@ -0,0 +1,125 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $values = self::values();
+
+ foreach ($values as $value) {
+ yield $value => [
+ $value,
+ ];
+ }
+
+ foreach ($values as $value) {
+ $valueWithVPrefix = \preg_replace(
+ '/^~/',
+ '~v',
+ $value,
+ );
+
+ yield $valueWithVPrefix => [
+ $valueWithVPrefix,
+ ];
+ }
+ }
+
+ /**
+ * @return list
+ */
+ private static function values(): array
+ {
+ $values = [
+ '0',
+ '1',
+ '999',
+ ];
+
+ $valuesWithMajor = \array_map(static function (string $value): string {
+ return \sprintf(
+ '~%s',
+ $value,
+ );
+ }, $values);
+
+ $appendComponent = static function (array $previousValues) use ($values): array {
+ $appended = [];
+
+ foreach ($previousValues as $otherValue) {
+ foreach ($values as $value) {
+ $appended[] = \sprintf(
+ '%s.%s',
+ $otherValue,
+ $value,
+ );
+ }
+ }
+
+ return $appended;
+ };
+
+ $valuesWithMajorAndMinor = $appendComponent($valuesWithMajor);
+ $valuesWithMajorMinorAndPatch = $appendComponent($valuesWithMajorAndMinor);
+ $valuesWithMajorMinorPatchAndFourth = $appendComponent($valuesWithMajorMinorAndPatch);
+
+ $stabilityFlags = [
+ 'alpha',
+ 'beta',
+ 'dev',
+ 'RC',
+ 'stable',
+ ];
+
+ $appendStability = static function (array $previousValues) use ($stabilityFlags): array {
+ $appended = [];
+
+ foreach ($previousValues as $otherValue) {
+ foreach ($stabilityFlags as $stabilityFlag) {
+ $appended[] = \sprintf(
+ '%s-%s',
+ $otherValue,
+ $stabilityFlag,
+ );
+ }
+ }
+
+ return $appended;
+ };
+
+ $valuesWithMajorAndStability = $appendStability($valuesWithMajor);
+ $valuesWithMajorMinorAndStability = $appendStability($valuesWithMajorAndMinor);
+ $valuesWithMajorMinorPatchAndStability = $appendStability($valuesWithMajorMinorAndPatch);
+ $valuesWithMajorMinorPatchFourthAndStability = $appendStability($valuesWithMajorMinorPatchAndFourth);
+
+ return \array_merge(
+ $valuesWithMajor,
+ $valuesWithMajorAndMinor,
+ $valuesWithMajorMinorAndPatch,
+ $valuesWithMajorMinorPatchAndFourth,
+ $valuesWithMajorAndStability,
+ $valuesWithMajorMinorAndStability,
+ $valuesWithMajorMinorPatchAndStability,
+ $valuesWithMajorMinorPatchFourthAndStability,
+ );
+ }
+}
diff --git a/test/DataProvider/WildCardVersionRangeProvider.php b/test/DataProvider/WildCardVersionRangeProvider.php
new file mode 100644
index 0000000..5899253
--- /dev/null
+++ b/test/DataProvider/WildCardVersionRangeProvider.php
@@ -0,0 +1,39 @@
+
+ */
+ public static function valid(): \Generator
+ {
+ $values = [
+ '*',
+ '1.*',
+ '1.0.*',
+ 'v1.*',
+ 'v1.0.*',
+ ];
+
+ foreach ($values as $value) {
+ yield $value => [
+ $value,
+ ];
+ }
+ }
+}
diff --git a/test/Unit/Composer/Exception/InvalidTildeVersionRangeTest.php b/test/Unit/Composer/Exception/InvalidTildeVersionRangeTest.php
new file mode 100644
index 0000000..808351e
--- /dev/null
+++ b/test/Unit/Composer/Exception/InvalidTildeVersionRangeTest.php
@@ -0,0 +1,38 @@
+word();
+
+ $exception = Composer\Exception\InvalidTildeVersionRange::fromString($value);
+
+ $message = \sprintf(
+ 'Value "%s" does not appear to be a valid value.',
+ $value,
+ );
+
+ self::assertSame($message, $exception->getMessage());
+ }
+}
diff --git a/test/Unit/Composer/TildeVersionRangeTest.php b/test/Unit/Composer/TildeVersionRangeTest.php
new file mode 100644
index 0000000..31052ed
--- /dev/null
+++ b/test/Unit/Composer/TildeVersionRangeTest.php
@@ -0,0 +1,48 @@
+expectException(Composer\Exception\InvalidTildeVersionRange::class);
+
+ Composer\TildeVersionRange::fromString($value);
+ }
+
+ #[Framework\Attributes\DataProviderExternal(DataProvider\TildeVersionRangeProvider::class, 'valid')]
+ public function testFromStringReturnsTildeVersionRange(string $value): void
+ {
+ $versionConstraint = Composer\TildeVersionRange::fromString($value);
+
+ self::assertSame($value, $versionConstraint->toString());
+ }
+}
diff --git a/test/Unit/ExampleTest.php b/test/Unit/ExampleTest.php
deleted file mode 100644
index f80fb17..0000000
--- a/test/Unit/ExampleTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-sentence();
-
- $example = Example::fromString($value);
-
- self::assertSame($value, $example->toString());
- }
-}