diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 189105d..2f3eecb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,5 +12,3 @@ jobs: uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main cs: uses: innmind/github-workflows/.github/workflows/cs.yml@main - with: - php-version: '8.2' diff --git a/.github/workflows/extensive.yml b/.github/workflows/extensive.yml new file mode 100644 index 0000000..257f139 --- /dev/null +++ b/.github/workflows/extensive.yml @@ -0,0 +1,12 @@ +name: Extensive CI + +on: + push: + tags: + - '*' + paths: + - '.github/workflows/extensive.yml' + +jobs: + blackbox: + uses: innmind/github-workflows/.github/workflows/extensive.yml@main diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b4cd1d..baa209f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,26 @@ ## [Unreleased] +### Added + +- `Innmind\UrlTemplate\Template::attempt()` +- `Innmind\UrlTemplate\Template::expansion()` + ### Changed -- Requires `innmind/immutable:~5.18` +- Requires `innmind/immutable:~6.0` +- Requires `innmind/url:~5.0` +- Requires PHP `8.4` +- `Innmind\UrlTemplate\Template::extract()` now returns an `Innmind\Immutable\Attempt` +- `Innmind\UrlTemplate\Template::matches()` now returns an `Innmind\Immutable\Attempt` + +### Removed + +- `Innmind\UrlTemplate\Template::expand()`, use `::expansion()` instead +- `Innmind\UrlTemplate\Exception\Exception` +- `Innmind\UrlTemplate\Exception\LogicException` +- `Innmind\UrlTemplate\Exception\DomainException` +- `Innmind\UrlTemplate\Exception\ExplodeExpressionCantBeMatched` ## 3.1.0 - 2023-09-16 diff --git a/composer.json b/composer.json index df5f200..b5a5c0d 100644 --- a/composer.json +++ b/composer.json @@ -15,9 +15,9 @@ "issues": "http://github.com/Innmind/UrlTemplate/issues" }, "require": { - "php": "~8.2", - "innmind/url": "~4.1", - "innmind/immutable": "~5.18" + "php": "~8.4", + "innmind/url": "~5.0", + "innmind/immutable": "~6.0" }, "autoload": { "psr-4": { @@ -30,7 +30,7 @@ } }, "require-dev": { - "innmind/static-analysis": "^1.2.1", + "innmind/static-analysis": "~1.3", "innmind/black-box": "~6.5", "innmind/coding-standard": "~2.0" } diff --git a/src/Exception/DomainException.php b/src/Exception/DomainException.php deleted file mode 100644 index 3e994a0..0000000 --- a/src/Exception/DomainException.php +++ /dev/null @@ -1,8 +0,0 @@ - $expressions + * @param Map $values + * @param Map> $lists + * @param Map> $keys + */ + private function __construct( + private Str $template, + private Sequence $expressions, + private Map $values, + private Map $lists, + private Map $keys, + ) { + } + + /** + * @psalm-pure + * @internal + * + * @param Sequence $expressions + */ + public static function of(Str $template, Sequence $expressions): self + { + return new self( + $template, + $expressions, + Map::of(), + Map::of(), + Map::of(), + ); + } + + /** + * @no-named-arguments + * + * @param non-empty-string $name + */ + public function with(string $name, string ...$values): self + { + if (\count($values) === 1) { + return new self( + $this->template, + $this->expressions, + ($this->values)($name, $values[0]), + $this->lists->remove($name), + $this->keys->remove($name), + ); + } + + return new self( + $this->template, + $this->expressions, + $this->values->remove($name), + ($this->lists)($name, $values), + $this->keys->remove($name), + ); + } + + /** + * @no-named-arguments + * + * @param non-empty-string $name + * @param array{string, string} ...$keys + */ + public function withKeys(string $name, array ...$keys): self + { + return new self( + $this->template, + $this->expressions, + $this->values->remove($name), + $this->lists->remove($name), + ($this->keys)($name, \array_map( + static fn($pair) => [Name::of($pair[0]), $pair[1]], + $keys, + )), + ); + } + + public function expand(): Url + { + $url = $this->expressions->reduce( + $this->template, + fn(Str $template, $expression) => $template->replace( + $expression->toString(), + $expression->expand( + $this->values, + $this->lists, + $this->keys, + ), + ), + ); + + return Url::of($url->toString()); + } +} diff --git a/src/Expression.php b/src/Expression.php index 240084b..9c6c6a2 100644 --- a/src/Expression.php +++ b/src/Expression.php @@ -3,10 +3,10 @@ namespace Innmind\UrlTemplate; +use Innmind\UrlTemplate\Expression\Name; use Innmind\Immutable\{ Map, - Str, - Maybe, + Attempt, }; /** @@ -15,19 +15,18 @@ */ interface Expression { + public function expansion(): Expression\Expansion; + /** - * @psalm-pure - * - * @return Maybe + * @param Map $values + * @param Map> $lists + * @param Map> $keys */ - public static function of(Str $string): Maybe; - - public function expansion(): Expression\Expansion; + public function expand(Map $values, Map $lists, Map $keys): string; /** - * @param Map|list> $variables + * @return Attempt */ - public function expand(Map $variables): string; - public function regex(): string; + public function regex(): Attempt; public function toString(): string; } diff --git a/src/Expression/Level1.php b/src/Expression/Level1.php index e63f980..ca67c65 100644 --- a/src/Expression/Level1.php +++ b/src/Expression/Level1.php @@ -10,33 +10,30 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Level1 implements Expression { - private Name $name; - private UrlEncode $encode; - - private function __construct(Name $name) - { - $this->name = $name; - $this->encode = new UrlEncode; + private function __construct( + private Name $name, + private UrlEncode $encode, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::one($string, Expansion::simple)->map( - static fn($name) => new self($name), - ); + return Name::one($string, Expansion::simple) + ->map(self::named(...)); } /** @@ -44,7 +41,12 @@ public static function of(Str $string): Maybe */ public static function named(Name $name): self { - return new self($name); + return new self($name, UrlEncode::everything); + } + + public function name(): Name + { + return $this->name; } #[\Override] @@ -54,22 +56,25 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - /** @psalm-suppress InvalidArgument Because of the filter */ - return $variables + return $values ->get($this->name->toString()) - ->filter(\is_string(...)) ->match( - fn(string $variable) => ($this->encode)($variable), + $this->encode->encode(...), static fn() => '', ); } + public function encode(string $string): string + { + return $this->encode->encode($string); + } + #[\Override] - public function regex(): string + public function regex(): Attempt { - return "(?<{$this->name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)"; + return Attempt::result("(?<{$this->name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)"); } #[\Override] diff --git a/src/Expression/Level2/Fragment.php b/src/Expression/Level2/Fragment.php index a2c5f97..e03cb5d 100644 --- a/src/Expression/Level2/Fragment.php +++ b/src/Expression/Level2/Fragment.php @@ -12,33 +12,33 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Fragment implements Expression { - private Name $name; - private UrlEncode $encode; - - private function __construct(Name $name) - { - $this->name = $name; - $this->encode = UrlEncode::allowReservedCharacters(); + private function __construct( + private Name $name, + private UrlEncode $encode, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::one($string, Expansion::fragment)->map( - static fn($name) => new self($name), - ); + return Name::one($string, Expansion::fragment) + ->map(static fn($name) => new self( + $name, + UrlEncode::allowReservedCharacters, + )); } #[\Override] @@ -48,22 +48,21 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - /** @psalm-suppress InvalidArgument Because of the filter */ - return $variables + return $values ->get($this->name->toString()) - ->filter(\is_string(...)) + ->map($this->encode->encode(...)) ->match( - fn(string $variable) => '#'.($this->encode)($variable), + static fn(string $variable) => '#'.$variable, static fn() => '', ); } #[\Override] - public function regex(): string + public function regex(): Attempt { - return "\#(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"; + return Attempt::result("\#(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"); } #[\Override] diff --git a/src/Expression/Level2/Reserved.php b/src/Expression/Level2/Reserved.php index 6c33602..c04696a 100644 --- a/src/Expression/Level2/Reserved.php +++ b/src/Expression/Level2/Reserved.php @@ -12,33 +12,30 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Reserved implements Expression { - private Name $name; - private UrlEncode $encode; - - private function __construct(Name $name) - { - $this->name = $name; - $this->encode = UrlEncode::allowReservedCharacters(); + private function __construct( + private Name $name, + private UrlEncode $encode, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::one($string, Expansion::reserved)->map( - static fn($name) => new self($name), - ); + return Name::one($string, Expansion::reserved) + ->map(self::named(...)); } /** @@ -46,7 +43,12 @@ public static function of(Str $string): Maybe */ public static function named(Name $name): self { - return new self($name); + return new self($name, UrlEncode::allowReservedCharacters); + } + + public function name(): Name + { + return $this->name; } #[\Override] @@ -56,22 +58,25 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - /** @psalm-suppress InvalidArgument Because of the filter */ - return $variables + return $values ->get($this->name->toString()) - ->filter(\is_string(...)) ->match( - fn(string $variable) => ($this->encode)($variable), + $this->encode->encode(...), static fn() => '', ); } + public function encode(string $string): string + { + return $this->encode->encode($string); + } + #[\Override] - public function regex(): string + public function regex(): Attempt { - return "(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"; + return Attempt::result("(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]*)"); } #[\Override] diff --git a/src/Expression/Level3.php b/src/Expression/Level3.php index 28ca6ec..a4f5cbe 100644 --- a/src/Expression/Level3.php +++ b/src/Expression/Level3.php @@ -8,38 +8,31 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Level3 implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - $this->expressions = $this->names->map(Level1::named(...)); } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::simple)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::simple) + ->map(static fn($names) => new self($names)); } #[\Override] @@ -49,24 +42,27 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), - ); + $expanded = $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of(',')->join($expanded)->toString(); } #[\Override] - public function regex(): string + public function regex(): Attempt { /** @psalm-suppress InvalidArgument */ - return Str::of(',') - ->join($this->names->map( - static fn(Name $name) => "(?<{$name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)", - )) - ->toString(); + return Attempt::result( + Str::of(',') + ->join($this->names->map( + static fn(Name $name) => "(?<{$name->toString()}>[a-zA-Z0-9\%\-\.\_\~]*)", + )) + ->toString(), + ); } #[\Override] diff --git a/src/Expression/Level3/Fragment.php b/src/Expression/Level3/Fragment.php index 506bc4f..fbad1f6 100644 --- a/src/Expression/Level3/Fragment.php +++ b/src/Expression/Level3/Fragment.php @@ -13,39 +13,31 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Fragment implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level2\Reserved::named(...)); } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::fragment)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::fragment) + ->map(static fn($names) => new self($names)); } #[\Override] @@ -55,11 +47,12 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), - ); + $expanded = $this + ->names + ->map(Level2\Reserved::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of(',') ->join($expanded) @@ -68,14 +61,16 @@ public function expand(Map $variables): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of(',') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->prepend('\#') - ->toString(); + return $this + ->names + ->map(Level2\Reserved::named(...)) + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of(',')->join(...)) + ->map(static fn($regex) => $regex->prepend('\#')->toString()); } #[\Override] diff --git a/src/Expression/Level3/Label.php b/src/Expression/Level3/Label.php index 1e94e75..3195814 100644 --- a/src/Expression/Level3/Label.php +++ b/src/Expression/Level3/Label.php @@ -13,39 +13,31 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Label implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level1::named(...)); } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::label)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::label) + ->map(static fn($names) => new self($names)); } #[\Override] @@ -55,11 +47,12 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), - ); + $expanded = $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of('.') ->join($expanded) @@ -68,15 +61,21 @@ public function expand(Map $variables): string } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of('.') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->replace('\.', '') - ->prepend('\.') - ->toString(); + return $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of('.')->join(...)) + ->map( + static fn($regex) => $regex + ->replace('\.', '') + ->prepend('\.') + ->toString(), + ); } #[\Override] diff --git a/src/Expression/Level3/NamedValues.php b/src/Expression/Level3/NamedValues.php index b518f82..10f3b24 100644 --- a/src/Expression/Level3/NamedValues.php +++ b/src/Expression/Level3/NamedValues.php @@ -3,57 +3,42 @@ namespace Innmind\UrlTemplate\Expression\Level3; -use Innmind\UrlTemplate\{ - Expression, - Expression\Name, - Expression\Expansion, - Expression\Level1, +use Innmind\UrlTemplate\Expression\{ + Name, + Expansion, + Level1, }; use Innmind\Immutable\{ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ -final class NamedValues implements Expression +final class NamedValues { - private Expansion $expansion; - /** @var Sequence */ - private Sequence $names; - /** @var Map */ - private Map $expressions; - private bool $keyOnlyWhenEmpty = false; - /** * @param Sequence $names */ - public function __construct(Expansion $expansion, Sequence $names) - { - $this->expansion = $expansion; - $this->names = $names; - /** @var Map */ - $this->expressions = Map::of( - ...$this - ->names - ->map(static fn($name) => [ - $name->toString(), - Level1::named($name), - ]) - ->toList(), - ); + private function __construct( + private Expansion $expansion, + private Sequence $names, + private bool $keyOnlyWhenEmpty = false, + ) { } /** * @psalm-pure + * + * @param Sequence $names */ - #[\Override] - public static function of(Str $string): Maybe + public static function parameters(Sequence $names): self { - throw new \LogicException('should not be used directly'); + return new self(Expansion::parameter, $names, true); } /** @@ -61,36 +46,43 @@ public static function of(Str $string): Maybe * * @param Sequence $names */ - public static function keyOnlyWhenEmpty(Expansion $expansion, Sequence $names): self + public static function query(Sequence $names): self { - $self = new self($expansion, $names); - $self->keyOnlyWhenEmpty = true; - - return $self; + return new self(Expansion::query, $names, false); } - #[\Override] - public function expansion(): Expansion + /** + * @psalm-pure + * + * @param Sequence $names + */ + public static function queryContinuation(Sequence $names): self { - return $this->expansion; + return new self(Expansion::queryContinuation, $names, false); } - #[\Override] - public function expand(Map $variables): string + /** + * @param Map $values + * @param Map> $lists + * @param Map> $keys + */ + public function expand(Map $values, Map $lists, Map $keys): string { - /** @var Sequence */ - $expanded = $this->expressions->reduce( - Sequence::strings(), - function(Sequence $expanded, string $name, Expression $expression) use ($variables): Sequence { - $value = Str::of($expression->expand($variables)); - - if ($value->empty() && $this->keyOnlyWhenEmpty) { - return ($expanded)($name); - } - - return ($expanded)("$name={$value->toString()}"); - }, - ); + $expanded = $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => [ + $expression->name()->toString(), + Str::of($expression->expand($values, $lists, $keys)), + ]) + ->map(fn($pair) => match ([$pair[1]->empty(), $this->keyOnlyWhenEmpty]) { + [true, true] => $pair[0], + default => \sprintf( + '%s=%s', + $pair[0], + $pair[1]->toString(), + ), + }); return Str::of($this->expansion->continuation()->toString()) ->join($expanded) @@ -98,23 +90,27 @@ function(Sequence $expanded, string $name, Expression $expression) use ($variabl ->toString(); } - #[\Override] - public function regex(): string + /** + * @return Attempt + */ + public function regex(): Attempt { - return Str::of($this->expansion->continuation()->regex()) - ->join($this->names->map( - fn($name) => \sprintf( + return $this + ->names + ->map(fn($name) => Level1::named($name)->regex()->map( + fn($regex) => \sprintf( '%s=%s%s', $name->toString(), $this->keyOnlyWhenEmpty ? '?' : '', - Level1::named($name)->regex(), + $regex, ), )) - ->prepend($this->expansion->regex()) - ->toString(); + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of($this->expansion->continuation()->regex())->join(...)) + ->map(fn($regex) => $regex->prepend($this->expansion->regex())->toString()); } - #[\Override] public function toString(): string { /** @psalm-suppress InvalidArgument */ diff --git a/src/Expression/Level3/Parameters.php b/src/Expression/Level3/Parameters.php index a5d408b..b69d8c5 100644 --- a/src/Expression/Level3/Parameters.php +++ b/src/Expression/Level3/Parameters.php @@ -12,34 +12,30 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Parameters implements Expression { - private Expression $expression; - - /** - * @param Sequence $names - */ - private function __construct(Sequence $names) - { - $this->expression = NamedValues::keyOnlyWhenEmpty(Expansion::parameter, $names); + private function __construct( + private NamedValues $expression, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::parameter)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::parameter) + ->map(NamedValues::parameters(...)) + ->map(static fn($names) => new self($names)); } /** @@ -47,7 +43,7 @@ public static function of(Str $string): Maybe */ public static function named(Name $name): self { - return new self(Sequence::of($name)); + return new self(NamedValues::parameters(Sequence::of($name))); } #[\Override] @@ -57,13 +53,13 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level3/Path.php b/src/Expression/Level3/Path.php index 2b80602..54423b6 100644 --- a/src/Expression/Level3/Path.php +++ b/src/Expression/Level3/Path.php @@ -13,39 +13,31 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Path implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level1::named(...)); } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::path)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::path) + ->map(static fn($names) => new self($names)); } #[\Override] @@ -55,25 +47,30 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { return Str::of('/') - ->join($this->expressions->map( - static fn($expression) => $expression->expand($variables), - )) + ->join( + $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)), + ) ->prepend('/') ->toString(); } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of('/') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->prepend('/') - ->toString(); + return $this + ->names + ->map(Level1::named(...)) + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of('/')->join(...)) + ->map(static fn($regex) => $regex->prepend('/')->toString()); } #[\Override] diff --git a/src/Expression/Level3/Query.php b/src/Expression/Level3/Query.php index e89c76a..b1df6a2 100644 --- a/src/Expression/Level3/Query.php +++ b/src/Expression/Level3/Query.php @@ -12,34 +12,30 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Query implements Expression { - private Expression $expression; - - /** - * @param Sequence $names - */ - private function __construct(Sequence $names) - { - $this->expression = new NamedValues(Expansion::query, $names); + private function __construct( + private NamedValues $expression, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::query)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::query) + ->map(NamedValues::query(...)) + ->map(static fn($expression) => new self($expression)); } /** @@ -47,7 +43,7 @@ public static function of(Str $string): Maybe */ public static function named(Name $name): self { - return new self(Sequence::of($name)); + return new self(NamedValues::query(Sequence::of($name))); } #[\Override] @@ -57,13 +53,13 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level3/QueryContinuation.php b/src/Expression/Level3/QueryContinuation.php index df56af6..76c6ef2 100644 --- a/src/Expression/Level3/QueryContinuation.php +++ b/src/Expression/Level3/QueryContinuation.php @@ -12,39 +12,35 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class QueryContinuation implements Expression { - private Expression $expression; - - /** - * @param Sequence $names - */ - private function __construct(Sequence $names) - { - $this->expression = new NamedValues(Expansion::queryContinuation, $names); + private function __construct( + private NamedValues $expression, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::queryContinuation)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::queryContinuation) + ->map(NamedValues::queryContinuation(...)) + ->map(static fn($expression) => new self($expression)); } public static function named(Name $name): self { - return new self(Sequence::of($name)); + return new self(NamedValues::queryContinuation(Sequence::of($name))); } #[\Override] @@ -54,13 +50,13 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level3/Reserved.php b/src/Expression/Level3/Reserved.php index 525ac87..301a68c 100644 --- a/src/Expression/Level3/Reserved.php +++ b/src/Expression/Level3/Reserved.php @@ -13,39 +13,31 @@ Map, Sequence, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Reserved implements Expression { - /** @var Sequence */ - private Sequence $names; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $names */ - private function __construct(Sequence $names) + private function __construct(private Sequence $names) { - $this->names = $names; - /** @var Sequence */ - $this->expressions = $this->names->map(Level2\Reserved::named(...)); } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ - return Name::many($string, Expansion::reserved)->map( - static fn($names) => new self($names), - ); + return Name::many($string, Expansion::reserved) + ->map(static fn($names) => new self($names)); } #[\Override] @@ -55,23 +47,27 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), - ); + $expanded = $this + ->names + ->map(Level2\Reserved::named(...)) + ->map(static fn($expression) => $expression->expand($values, $lists, $keys)); return Str::of(',')->join($expanded)->toString(); } #[\Override] - public function regex(): string + public function regex(): Attempt { - return Str::of(',') - ->join($this->expressions->map( - static fn($expression) => $expression->regex(), - )) - ->toString(); + return $this + ->names + ->map(Level2\Reserved::named(...)) + ->map(static fn($expression) => $expression->regex()) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map(Str::of(',')->join(...)) + ->map(static fn($regex) => $regex->toString()); } #[\Override] diff --git a/src/Expression/Level4.php b/src/Expression/Level4.php index 6599f67..e8dc235 100644 --- a/src/Expression/Level4.php +++ b/src/Expression/Level4.php @@ -3,45 +3,41 @@ namespace Innmind\UrlTemplate\Expression; -use Innmind\UrlTemplate\{ - Expression, - Exception\ExplodeExpressionCantBeMatched, -}; +use Innmind\UrlTemplate\Expression; use Innmind\Immutable\{ Map, Str, Sequence, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Level4 implements Expression { - private Name $name; - private Expression $expression; - /** @var ?positive-int */ - private ?int $limit = null; - private bool $explode = false; - private Expansion $expansion; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); - $this->expansion = Expansion::simple; + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Level1|Level2\Reserved $expression, + private ?int $limit, + private bool $explode, + private Expansion $expansion, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Level4\Parse::of( $string, - static fn(Name $name) => new self($name), + self::named(...), self::explode(...), self::limit(...), Expansion::simple, @@ -51,14 +47,16 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self( + Level1::named($name), + $limit, + false, + Expansion::simple, + ); } /** @@ -66,10 +64,12 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self( + Level1::named($name), + null, + true, + Expansion::simple, + ); } /** @@ -77,7 +77,17 @@ public static function explode(Name $name): self */ public static function named(Name $name): self { - return new self($name); + return new self( + Level1::named($name), + null, + false, + Expansion::simple, + ); + } + + public function name(): Name + { + return $this->expression->name(); } #[\Override] @@ -88,98 +98,108 @@ public function expansion(): Expansion public function withExpansion(Expansion $expansion): self { - $self = clone $this; - $self->expansion = $expansion; - - return $self; + return new self( + $this->expression, + $this->limit, + $this->explode, + $expansion, + ); } /** * Not ideal technic but didn't find a better to reduce duplicated code * @internal * - * @param pure-callable(Name): Expression $expression + * @param pure-callable(Name): Level2\Reserved $expression */ public function withExpression(callable $expression): self { - $self = clone $this; - $self->expression = $expression($self->name); - - return $self; + return new self( + $expression($this->name()), + $this->limit, + $this->explode, + $this->expansion, + ); } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variables, $variable); - } - - if ($this->explode) { - return $this->explodeList($variables, [$variable]); - } - - if ($this->mustLimit()) { - $value = Str::of($variable)->take($this->limit); - $value = $this->expression->expand( - ($variables)($this->name->toString(), $value->toString()), + $name = $this->name()->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + if ($this->mustLimit()) { + $value = Str::of($value)->take($this->limit)->toString(); + } + + $value = Str::of($this->expression->encode($value)); + + return "{$this->expansion->toString()}$value"; + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', ); - } else { - $value = $this->expression->expand($variables); - } - - return "{$this->expansion->toString()}$value"; } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = $this + ->expression + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { $regex = $this->expression->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '%s%s', $this->expansion->regex(), $regex, - ); + )); } #[\Override] public function toString(): string { if ($this->mustLimit()) { - return "{{$this->expansion->toString()}{$this->name->toString()}:{$this->limit}}"; + return "{{$this->expansion->toString()}{$this->name()->toString()}:{$this->limit}}"; } if ($this->explode) { - return "{{$this->expansion->toString()}{$this->name->toString()}*}"; + return "{{$this->expansion->toString()}{$this->name()->toString()}*}"; } - return "{{$this->expansion->toString()}{$this->name->toString()}}"; + return "{{$this->expansion->toString()}{$this->name()->toString()}}"; } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { @@ -187,13 +207,12 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string + private function expandList(array $variablesToExpand): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -201,21 +220,17 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); }, ); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation $expanded = $flattenedVariables->map( - function($variableToExpand) use ($variables): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - return $this->expression->expand( - ($variables)($this->name->toString(), $variableToExpand), - ); - }, + $this->expression->encode(...), ); return $this->separator() @@ -225,35 +240,26 @@ function($variableToExpand) use ($variables): string { } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string + private function explodeList(array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map( - function($variableToExpand) use ($variables): string { - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $variableToExpand = $value; - } - - $variables = ($variables)($this->name->toString(), $variableToExpand); - - $value = $this->expression->expand($variables); - - if (isset($name)) { - /** @psalm-suppress MixedArgument */ - $name = Name::of($name); - $value = \sprintf( - '%s=%s', - $name->toString(), - $value, - ); - } - - return $value; - }, - ); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => $this->expression->encode($value), + default => [ + $value[0], + $this->expression->encode($value[1]), + ], + }) + ->map(static fn($value) => match (true) { + \is_string($value) => $value, + default => \sprintf( + '%s=%s', + $value[0]->toString(), + $value[1], + ), + }); return $this->separator() ->join($expanded) diff --git a/src/Expression/Level4/Composite.php b/src/Expression/Level4/Composite.php index dd0a403..33fdba9 100644 --- a/src/Expression/Level4/Composite.php +++ b/src/Expression/Level4/Composite.php @@ -13,41 +13,41 @@ Sequence, Str, Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Composite implements Expression { - private Expansion $expansion; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $expressions */ - private function __construct(Expansion $expansion, Sequence $expressions) - { - $this->expansion = $expansion; - $this->expressions = $expressions; + private function __construct( + private Expansion $expansion, + private Sequence $expressions, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** @var Maybe */ return Maybe::just($string) ->filter(Expansion::matchesLevel4(...)) ->map(Expansion::simple->clean(...)) ->map(static fn($string) => $string->split(',')) + ->attempt(static fn() => new \LogicException('Cannot parse level 4 composite')) ->flatMap( static fn($expressions) => $expressions ->first() ->map(static fn($first) => $first->prepend('{')->append('}')) + ->attempt(static fn() => new \LogicException('First expression not found')) ->flatMap(Expressions::of(...)) ->flatMap( static fn($first) => self::parse($first, $expressions->drop(1)) @@ -66,10 +66,10 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { $expanded = $this->expressions->map( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), ); //potentially remove the lead characters from the expressions except for @@ -91,28 +91,34 @@ public function expand(Map $variables): string } #[\Override] - public function regex(): string + public function regex(): Attempt { $remaining = $this ->expressions ->drop(1) - ->map(function(Expression $expression): string { + ->map(function($expression) { if ($this->removeLead()) { - return Str::of($expression->regex())->drop(2)->toString(); + return $expression + ->regex() + ->map(Str::of(...)) + ->map(static fn($regex) => $regex->drop(2)->toString()); } return $expression->regex(); }); - return Str::of($this->expansion()->separatorRegex()) - ->join( - $this - ->expressions - ->take(1) - ->map(static fn($expression) => $expression->regex()) - ->append($remaining), - ) - ->toString(); + return $this + ->expressions + ->take(1) + ->map(static fn($expression) => $expression->regex()) + ->append($remaining) + ->sink(Sequence::strings()) + ->attempt(static fn($regexes, $regex) => $regex->map($regexes)) + ->map( + fn($regexes) => Str::of($this->expansion()->separatorRegex()) + ->join($regexes) + ->toString(), + ); } #[\Override] @@ -148,20 +154,18 @@ public function toString(): string * * @param Sequence $expressions * - * @return Maybe> + * @return Attempt> */ - private static function parse(Expression $first, Sequence $expressions): Maybe + private static function parse(Expression $first, Sequence $expressions): Attempt { - /** @var Maybe> */ - return Maybe::all( - Maybe::just($first), - ...$expressions - ->map(static fn($expression) => $expression->prepend($first->expansion()->continuation()->toString())) - ->map(static fn($expression) => $expression->prepend('{')->append('}')) - ->map(Expressions::of(...)) - ->toList(), - ) - ->map(Sequence::of(...)); + return $expressions + ->map(static fn($expression) => $expression->prepend( + $first->expansion()->continuation()->toString(), + )) + ->map(static fn($expression) => $expression->prepend('{')->append('}')) + ->map(Expressions::of(...)) + ->sink(Sequence::of($first)) + ->attempt(static fn($expressions, $expression) => $expression->map($expressions)); } private function removeLead(): bool diff --git a/src/Expression/Level4/Fragment.php b/src/Expression/Level4/Fragment.php index ffc5d2b..b5cb78b 100644 --- a/src/Expression/Level4/Fragment.php +++ b/src/Expression/Level4/Fragment.php @@ -13,32 +13,34 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Fragment implements Expression { - private Expression $expression; - - private function __construct(Name $name) - { - $this->expression = Level4::named($name) - ->withExpansion(Expansion::fragment) - ->withExpression(Level2\Reserved::named(...)); + private function __construct( + private Level4 $expression, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + Level4::named($name) + ->withExpansion(Expansion::fragment) + ->withExpression(Level2\Reserved::named(...)), + ), self::explode(...), self::limit(...), Expansion::fragment, @@ -48,16 +50,15 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->expression = Level4::limit($name, $limit) - ->withExpansion(Expansion::fragment) - ->withExpression(Level2\Reserved::named(...)); - - return $self; + return new self( + Level4::limit($name, $limit) + ->withExpansion(Expansion::fragment) + ->withExpression(Level2\Reserved::named(...)), + ); } /** @@ -65,12 +66,11 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->expression = Level4::explode($name) - ->withExpansion(Expansion::fragment) - ->withExpression(Level2\Reserved::named(...)); - - return $self; + return new self( + Level4::explode($name) + ->withExpansion(Expansion::fragment) + ->withExpression(Level2\Reserved::named(...)), + ); } #[\Override] @@ -80,15 +80,15 @@ public function expansion(): Expansion } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] diff --git a/src/Expression/Level4/Label.php b/src/Expression/Level4/Label.php index ca02c63..5a72f8e 100644 --- a/src/Expression/Level4/Label.php +++ b/src/Expression/Level4/Label.php @@ -12,30 +12,32 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Label implements Expression { - private Expression $expression; - - private function __construct(Name $name) - { - $this->expression = Level4::named($name)->withExpansion(Expansion::label); + private function __construct( + private Level4 $expression, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + Level4::named($name)->withExpansion(Expansion::label), + ), self::explode(...), self::limit(...), Expansion::label, @@ -45,14 +47,13 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->expression = Level4::limit($name, $limit)->withExpansion(Expansion::label); - - return $self; + return new self( + Level4::limit($name, $limit)->withExpansion(Expansion::label), + ); } /** @@ -60,11 +61,9 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->expression = Level4::explode($name) - ->withExpansion(Expansion::label); - - return $self; + return new self( + Level4::explode($name)->withExpansion(Expansion::label), + ); } #[\Override] @@ -74,13 +73,13 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level4/Parameters.php b/src/Expression/Level4/Parameters.php index 0437c02..2035340 100644 --- a/src/Expression/Level4/Parameters.php +++ b/src/Expression/Level4/Parameters.php @@ -9,41 +9,40 @@ Expression\Expansion, Expression\Level1, Expression\Level3, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, Str, Sequence, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Parameters implements Expression { - private Name $name; - /** @var ?positive-int */ - private ?int $limit = null; - private bool $explode = false; - private Expression $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self($name, null, false), self::explode(...), self::limit(...), Expansion::parameter, @@ -53,14 +52,11 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self($name, $limit, false); } /** @@ -68,10 +64,7 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self($name, null, true); } #[\Override] @@ -81,56 +74,72 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variables, $variable); - } - - if ($this->explode) { - return $this->explodeList($variables, [$variable]); - } - - $value = Str::of($this->expression->expand($variables)); - - if ($this->mustLimit()) { - return ";{$this->name->toString()}={$value->take($this->limit)->toString()}"; - } - - return ";{$this->name->toString()}={$value->toString()}"; + $name = $this->name->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + $value = Str::of(Level1::named($this->name)->encode($value)); + + if ($this->mustLimit()) { + return \sprintf( + ';%s=%s', + $this->name->toString(), + $value->take($this->limit)->toString(), + ); + } + + return \sprintf( + ';%s=%s', + $this->name->toString(), + $value->toString(), + ); + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', + ); } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = Level1::named($this->name) + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { - $regex = $this->expression->regex(); + $regex = Level1::named($this->name)->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '\;%s=%s', $this->name->toString(), $regex, - ); + )); } #[\Override] @@ -148,7 +157,7 @@ public function toString(): string } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { @@ -156,13 +165,12 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string + private function expandList(array $variablesToExpand): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -170,20 +178,16 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); }, ); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation $expanded = $flattenedVariables->map( - function($variableToExpand) use ($variables): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - $variables = ($variables)($this->name->toString(), $variableToExpand); - - return $this->expression->expand($variables); - }, + Level1::named($this->name)->encode(...), ); return Str::of(',') @@ -193,26 +197,20 @@ function($variableToExpand) use ($variables): string { } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string + private function explodeList(array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map( - function($variableToExpand) use ($variables): string { - $name = $this->name; - - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $name = Name::of($name); - $variableToExpand = $value; - } - - $variables = ($variables)($name->toString(), $variableToExpand); - - return Level3\Parameters::named($name)->expand($variables); - }, - ); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => [$this->name, $value], + default => $value, + }) + ->map(static fn($pair) => Level3\Parameters::named($pair[0])->expand( + Map::of([$pair[0]->toString(), $pair[1]]), + Map::of(), + Map::of(), + )); return Str::of('')->join($expanded)->toString(); } diff --git a/src/Expression/Level4/Parse.php b/src/Expression/Level4/Parse.php index bbe0948..aead883 100644 --- a/src/Expression/Level4/Parse.php +++ b/src/Expression/Level4/Parse.php @@ -10,19 +10,24 @@ }; use Innmind\Immutable\{ Str, - Maybe, + Attempt, }; +/** + * @internal + */ final class Parse { /** * @psalm-pure + * @internal + * @template T of Expression * - * @param pure-callable(Name): Expression $standard - * @param pure-callable(Name): Expression $explode - * @param pure-callable(Name, positive-int): Expression $limit + * @param pure-callable(Name): T $standard + * @param pure-callable(Name): T $explode + * @param pure-callable(Name, int<1, max>): T $limit * - * @return Maybe + * @return Attempt */ public static function of( Str $string, @@ -30,40 +35,42 @@ public static function of( callable $explode, callable $limit, Expansion $expansion, - ): Maybe { + ): Attempt { return Name::one($string, $expansion) ->map($standard) - ->otherwise(static fn() => self::explode($string, $explode, $expansion)) - ->otherwise(static fn() => self::limit($string, $limit, $expansion)); + ->recover(static fn() => self::explode($string, $explode, $expansion)) + ->recover(static fn() => self::limit($string, $limit, $expansion)); } /** * @psalm-pure + * @template T of Expression * - * @param pure-callable(Name): Expression $explode + * @param pure-callable(Name): T $explode * - * @return Maybe + * @return Attempt */ private static function explode( Str $string, callable $explode, Expansion $expansion, - ): Maybe { + ): Attempt { return Name::explode($string, $expansion)->map($explode); } /** * @psalm-pure + * @template T of Expression * - * @param pure-callable(Name, positive-int): Expression $limit + * @param pure-callable(Name, int<1, max>): T $limit * - * @return Maybe + * @return Attempt */ private static function limit( Str $string, callable $limit, Expansion $expansion, - ): Maybe { + ): Attempt { return Name::limit($string, $expansion)->map( static fn($tuple) => $limit($tuple[0], $tuple[1]), ); diff --git a/src/Expression/Level4/Path.php b/src/Expression/Level4/Path.php index 3417f4d..0e5fb0b 100644 --- a/src/Expression/Level4/Path.php +++ b/src/Expression/Level4/Path.php @@ -12,30 +12,32 @@ use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Path implements Expression { - private Expression $expression; - - private function __construct(Name $name) - { - $this->expression = Level4::named($name)->withExpansion(Expansion::path); + private function __construct( + private Level4 $expression, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + Level4::named($name)->withExpansion(Expansion::path), + ), self::explode(...), self::limit(...), Expansion::path, @@ -45,14 +47,13 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->expression = Level4::limit($name, $limit)->withExpansion(Expansion::path); - - return $self; + return new self( + Level4::limit($name, $limit)->withExpansion(Expansion::path), + ); } /** @@ -60,11 +61,9 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->expression = Level4::explode($name) - ->withExpansion(Expansion::path); - - return $self; + return new self( + Level4::explode($name)->withExpansion(Expansion::path), + ); } #[\Override] @@ -74,13 +73,13 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] - public function regex(): string + public function regex(): Attempt { return $this->expression->regex(); } diff --git a/src/Expression/Level4/Query.php b/src/Expression/Level4/Query.php index 0f79a9d..a685778 100644 --- a/src/Expression/Level4/Query.php +++ b/src/Expression/Level4/Query.php @@ -9,41 +9,40 @@ Expression\Expansion, Expression\Level1, Expression\Level3, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, Str, Sequence, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Query implements Expression { - private Name $name; - /** @var ?positive-int */ - private ?int $limit = null; - private bool $explode = false; - private Expression $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self($name, null, false), self::explode(...), self::limit(...), Expansion::query, @@ -53,14 +52,11 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self($name, $limit, false); } /** @@ -68,10 +64,7 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self($name, null, true); } #[\Override] @@ -81,56 +74,72 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variables, $variable); - } - - if ($this->explode) { - return $this->explodeList($variables, [$variable]); - } - - $value = Str::of($this->expression->expand($variables)); - - if ($this->mustLimit()) { - return "?{$this->name->toString()}={$value->take($this->limit)->toString()}"; - } - - return "?{$this->name->toString()}={$value->toString()}"; + $name = $this->name->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + $value = Str::of(Level1::named($this->name)->encode($value)); + + if ($this->mustLimit()) { + return \sprintf( + '?%s=%s', + $this->name->toString(), + $value->take($this->limit)->toString(), + ); + } + + return \sprintf( + '?%s=%s', + $this->name->toString(), + $value->toString(), + ); + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', + ); } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = Level1::named($this->name) + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { - $regex = $this->expression->regex(); + $regex = Level1::named($this->name)->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '\?%s=%s', $this->name->toString(), $regex, - ); + )); } #[\Override] @@ -148,7 +157,7 @@ public function toString(): string } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { @@ -156,13 +165,12 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string + private function expandList(array $variablesToExpand): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -170,20 +178,17 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand) use ($variables): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - - $variables = ($variables)($this->name->toString(), $variableToExpand); - - return $this->expression->expand($variables); - }); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation + $expanded = $flattenedVariables->map( + Level1::named($this->name)->encode(...), + ); return Str::of(',') ->join($expanded) @@ -192,28 +197,22 @@ static function($variableToExpand): Sequence { } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string + private function explodeList(array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map(function($variableToExpand) use ($variables): string { - $name = $this->name; - - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $name = Name::of($name); - $variableToExpand = $value; - } - - $variables = ($variables)($name->toString(), $variableToExpand); - - $value = Level3\Query::named($name)->expand($variables); - - // the substring is here to remove the '?' as it should be a '&' - // done below in the join - return Str::of($value)->drop(1)->toString(); - }); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => [$this->name, $value], + default => $value, + }) + ->map(static fn($pair) => Level3\Query::named($pair[0])->expand( + Map::of([$pair[0]->toString(), $pair[1]]), + Map::of(), + Map::of(), + )) + ->map(Str::of(...)) + ->map(static fn($value) => $value->drop(1)->toString()); // to remove the '?' as it should be a '&' done below in the join return Str::of('&') ->join($expanded) diff --git a/src/Expression/Level4/QueryContinuation.php b/src/Expression/Level4/QueryContinuation.php index 8673b21..ef446a4 100644 --- a/src/Expression/Level4/QueryContinuation.php +++ b/src/Expression/Level4/QueryContinuation.php @@ -9,41 +9,40 @@ Expression\Expansion, Expression\Level1, Expression\Level3, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, Str, Sequence, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class QueryContinuation implements Expression { - private Name $name; - /** @var ?positive-int */ - private ?int $limit = null; - private bool $explode = false; - private Expression $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level1::named($name); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self($name, null, false), self::explode(...), self::limit(...), Expansion::queryContinuation, @@ -53,14 +52,11 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - - return $self; + return new self($name, $limit, false); } /** @@ -68,10 +64,7 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - - return $self; + return new self($name, null, true); } #[\Override] @@ -81,56 +74,72 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - $variable = $variables->get($this->name->toString())->match( - static fn($variable) => $variable, - static fn() => null, - ); - - if (\is_null($variable)) { - return ''; - } - - if (\is_array($variable)) { - return $this->expandList($variables, $variable); - } - - if ($this->explode) { - return $this->explodeList($variables, [$variable]); - } - - $value = Str::of($this->expression->expand($variables)); - - if ($this->mustLimit()) { - return "&{$this->name->toString()}={$value->take($this->limit)->toString()}"; - } - - return "&{$this->name->toString()}={$value->toString()}"; + $name = $this->name->toString(); + + return $lists + ->get($name) + ->otherwise(static fn() => $keys->get($name)) + ->map($this->expandList(...)) + ->otherwise( + fn() => $values + ->get($name) + ->map(function($value) { + if ($this->explode) { + return $this->explodeList([$value]); + } + + $value = Str::of(Level1::named($this->name)->encode($value)); + + if ($this->mustLimit()) { + return \sprintf( + '&%s=%s', + $this->name->toString(), + $value->take($this->limit)->toString(), + ); + } + + return \sprintf( + '&%s=%s', + $this->name->toString(), + $value->toString(), + ); + }), + ) + ->match( + static fn($value) => $value, + static fn() => '', + ); } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if ($this->mustLimit()) { // replace '*' match by the actual limit - $regex = Str::of($this->expression->regex()) - ->dropEnd(2) - ->append("{{$this->limit}})") - ->toString(); + $regex = Level1::named($this->name) + ->regex() + ->map(Str::of(...)) + ->map( + fn($regex) => $regex + ->dropEnd(2) + ->append("{{$this->limit}})") + ->toString(), + ); } else { - $regex = $this->expression->regex(); + $regex = Level1::named($this->name)->regex(); } - return \sprintf( + return $regex->map(fn($regex) => \sprintf( '\&%s=%s', $this->name->toString(), $regex, - ); + )); } #[\Override] @@ -148,7 +157,7 @@ public function toString(): string } /** - * @psalm-assert-if-true positive-int $this->limit + * @psalm-assert-if-true int<1, max> $this->limit */ private function mustLimit(): bool { @@ -156,13 +165,12 @@ private function mustLimit(): bool } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function expandList(Map $variables, array $variablesToExpand): string + private function expandList(array $variablesToExpand): string { if ($this->explode) { - return $this->explodeList($variables, $variablesToExpand); + return $this->explodeList($variablesToExpand); } $flattenedVariables = Sequence::of(...$variablesToExpand)->flatMap( @@ -170,19 +178,17 @@ static function($variableToExpand): Sequence { if (\is_array($variableToExpand)) { [$name, $variableToExpand] = $variableToExpand; - return Sequence::of($name, $variableToExpand); + return Sequence::of($name->toString(), $variableToExpand); } return Sequence::of($variableToExpand); }, ); - $expanded = $flattenedVariables->map(function($variableToExpand) use ($variables): string { - // here we use the level1 expression to transform the variable to - // be expanded to its string representation - $variables = ($variables)($this->name->toString(), $variableToExpand); - - return $this->expression->expand($variables); - }); + // here we use the level1 expression to transform the variable to + // be expanded to its string representation + $expanded = $flattenedVariables->map( + Level1::named($this->name)->encode(...), + ); return Str::of(',') ->join($expanded) @@ -191,24 +197,20 @@ static function($variableToExpand): Sequence { } /** - * @param Map|list> $variables - * @param list|list $variablesToExpand + * @param list|list $variablesToExpand */ - private function explodeList(Map $variables, array $variablesToExpand): string + private function explodeList(array $variablesToExpand): string { - $expanded = Sequence::of(...$variablesToExpand)->map(function($variableToExpand) use ($variables): string { - $name = $this->name; - - if (\is_array($variableToExpand)) { - [$name, $value] = $variableToExpand; - $name = Name::of($name); - $variableToExpand = $value; - } - - $variables = ($variables)($name->toString(), $variableToExpand); - - return Level3\QueryContinuation::named($name)->expand($variables); - }); + $expanded = Sequence::of(...$variablesToExpand) + ->map(fn($value) => match (true) { + \is_string($value) => [$this->name, $value], + default => $value, + }) + ->map(static fn($pair) => Level3\QueryContinuation::named($pair[0])->expand( + Map::of([$pair[0]->toString(), $pair[1]]), + Map::of(), + Map::of(), + )); return Str::of('')->join($expanded)->toString(); } diff --git a/src/Expression/Level4/Reserved.php b/src/Expression/Level4/Reserved.php index 3f6b2a3..78a6dec 100644 --- a/src/Expression/Level4/Reserved.php +++ b/src/Expression/Level4/Reserved.php @@ -9,42 +9,47 @@ Expression\Expansion, Expression\Level2, Expression\Level4, - Exception\ExplodeExpressionCantBeMatched, }; use Innmind\Immutable\{ Map, Str, - Maybe, + Attempt, }; /** * @psalm-immutable + * @internal */ final class Reserved implements Expression { - private Name $name; - /** @var ?positive-int */ - private ?int $limit = null; - private bool $explode = false; - private Expression $expression; - - private function __construct(Name $name) - { - $this->name = $name; - $this->expression = Level4::named($name)->withExpression( - Level2\Reserved::named(...), - ); + /** + * @param ?int<1, max> $limit + */ + private function __construct( + private Name $name, + private ?int $limit, + private bool $explode, + private Level4 $expression, + ) { } /** * @psalm-pure + * + * @return Attempt */ - #[\Override] - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { return Parse::of( $string, - static fn(Name $name) => new self($name), + static fn(Name $name) => new self( + $name, + null, + false, + Level4::named($name)->withExpression( + Level2\Reserved::named(...), + ), + ), self::explode(...), self::limit(...), Expansion::reserved, @@ -54,17 +59,18 @@ public static function of(Str $string): Maybe /** * @psalm-pure * - * @param positive-int $limit + * @param int<1, max> $limit */ public static function limit(Name $name, int $limit): self { - $self = new self($name); - $self->limit = $limit; - $self->expression = Level4::limit($name, $limit)->withExpression( - Level2\Reserved::named(...), + return new self( + $name, + $limit, + false, + Level4::limit($name, $limit)->withExpression( + Level2\Reserved::named(...), + ), ); - - return $self; } /** @@ -72,13 +78,14 @@ public static function limit(Name $name, int $limit): self */ public static function explode(Name $name): self { - $self = new self($name); - $self->explode = true; - $self->expression = Level4::explode($name)->withExpression( - Level2\Reserved::named(...), + return new self( + $name, + null, + true, + Level4::explode($name)->withExpression( + Level2\Reserved::named(...), + ), ); - - return $self; } #[\Override] @@ -88,20 +95,20 @@ public function expansion(): Expansion } #[\Override] - public function expand(Map $variables): string + public function expand(Map $values, Map $lists, Map $keys): string { - return $this->expression->expand($variables); + return $this->expression->expand($values, $lists, $keys); } #[\Override] - public function regex(): string + public function regex(): Attempt { if ($this->explode) { - throw new ExplodeExpressionCantBeMatched; + return Attempt::error(new \LogicException('Explode expression cant be matched')); } if (\is_int($this->limit)) { - return "(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]{{$this->limit}})"; + return Attempt::result("(?<{$this->name->toString()}>[a-zA-Z0-9\%:/\?#\[\]@!\$&'\(\)\*\+,;=\-\.\_\~]{{$this->limit}})"); } return $this->expression->regex(); diff --git a/src/Expression/Name.php b/src/Expression/Name.php index fe8dd8b..932b7ad 100644 --- a/src/Expression/Name.php +++ b/src/Expression/Name.php @@ -3,10 +3,10 @@ namespace Innmind\UrlTemplate\Expression; -use Innmind\UrlTemplate\Exception\DomainException; use Innmind\Immutable\{ Str, Maybe, + Attempt, Sequence, }; @@ -16,26 +16,24 @@ */ final class Name { - /** @var non-empty-string */ - private string $value; - /** * @param non-empty-string $value */ - private function __construct(string $value) + private function __construct(private string $value) { - $this->value = $value; } /** * @psalm-pure + * + * @throws \DomainException */ public static function of(string $value): self { $characters = self::characters(); if (!Str::of($value)->matches("~^{$characters}\$~")) { - throw new DomainException($value); + throw new \DomainException($value); } /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ @@ -45,49 +43,59 @@ public static function of(string $value): self /** * @psalm-pure * - * @return Maybe + * @return Attempt */ public static function one( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ return Maybe::just($value) ->filter($expansion->matches(...)) ->map($expansion->clean(...)) ->map(static fn($value) => $value->toString()) - ->map(static fn($value) => new self($value)); + ->map(static fn($value) => new self($value)) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match %s expansion', + $value->toString(), + $expansion->name, + ))); } /** * @psalm-pure * - * @return Maybe + * @return Attempt */ public static function explode( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ return Maybe::just($value) ->filter($expansion->matchesExplode(...)) ->map($expansion->cleanExplode(...)) ->map(static fn($value) => $value->toString()) - ->map(static fn($value) => new self($value)); + ->map(static fn($value) => new self($value)) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match exploding %s expansion', + $value->toString(), + $expansion->name, + ))); } /** * @psalm-pure * - * @return Maybe + * @return Attempt}> */ public static function limit( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** * @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string - * @var Maybe + * @var Attempt}> */ return Maybe::just($value) ->filter($expansion->matchesLimit(...)) @@ -106,18 +114,23 @@ public static function limit( ->filter(static fn(int $limit) => $limit > 0) ->map(static fn($int) => [$name, $int]), ), - ); + ) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match limited %s expansion', + $value->toString(), + $expansion->name, + ))); } /** * @psalm-pure * - * @return Maybe> + * @return Attempt> */ public static function many( Str $value, Expansion $expansion, - ): Maybe { + ): Attempt { /** @psalm-suppress ArgumentTypeCoercion Because of the non-empty-string */ return Maybe::just($value) ->filter($expansion->matchesMany(...)) @@ -127,7 +140,12 @@ public static function many( ->split(',') ->map(static fn($value) => $value->toString()) ->map(static fn($value) => new self($value)), - ); + ) + ->attempt(static fn() => new \LogicException(\sprintf( + 'Value "%s" does not match many %s expansions', + $value->toString(), + $expansion->name, + ))); } /** diff --git a/src/Expressions.php b/src/Expressions.php index 59c186f..0ea5d50 100644 --- a/src/Expressions.php +++ b/src/Expressions.php @@ -6,7 +6,7 @@ use Innmind\Immutable\{ Sequence, Str, - Maybe, + Attempt, }; /** @@ -18,30 +18,27 @@ final class Expressions /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function of(Str $string): Maybe + public static function of(Str $string): Attempt { - /** - * @psalm-suppress MixedReturnTypeCoercion - * @var Maybe - */ - return self::expressions()->reduce( - Maybe::nothing(), - static fn(Maybe $expression, $attempt) => $expression->otherwise( - static fn() => $attempt($string), - ), - ); + return self::expressions() + ->lookup() + ->first() + ->attempt( + new \LogicException('Failed to parse template'), + static fn($expression) => $expression($string), + ); } /** * @psalm-pure * - * @return Sequence> + * @return Sequence> */ private static function expressions(): Sequence { - /** @var Sequence> */ + /** @var Sequence> */ return Sequence::of( Expression\Level4::of(...), Expression\Level4\Reserved::of(...), diff --git a/src/Template.php b/src/Template.php index 4bbe08f..16b20a9 100644 --- a/src/Template.php +++ b/src/Template.php @@ -3,16 +3,13 @@ namespace Innmind\UrlTemplate; -use Innmind\UrlTemplate\Exception\{ - ExplodeExpressionCantBeMatched, - DomainException, -}; use Innmind\Url\Url; use Innmind\Immutable\{ Map, Sequence, Str, Maybe, + Attempt, }; /** @@ -20,17 +17,13 @@ */ final class Template { - private Str $template; - /** @var Sequence */ - private Sequence $expressions; - /** * @param Sequence $expressions */ - private function __construct(Str $template, Sequence $expressions) - { - $this->template = $template; - $this->expressions = $expressions; + private function __construct( + private Str $template, + private Sequence $expressions, + ) { } /** @@ -38,22 +31,19 @@ private function __construct(Str $template, Sequence $expressions) * * @param literal-string $template * - * @throws DomainException + * @throws \Exception */ public static function of(string $template): self { - return self::maybe($template)->match( - static fn($self) => $self, - static fn() => throw new DomainException($template), - ); + return self::attempt($template)->unwrap(); } /** * @psalm-pure * - * @return Maybe + * @return Attempt */ - public static function maybe(string $template): Maybe + public static function attempt(string $template): Attempt { $template = Str::of($template); @@ -63,43 +53,50 @@ public static function maybe(string $template): Maybe } /** - * @param Map|list> $variables + * @psalm-pure + * + * @return Maybe */ - public function expand(Map $variables): Url + public static function maybe(string $template): Maybe { - $url = $this->expressions->reduce( - $this->template, - static function(Str $template, Expression $expression) use ($variables): Str { - return $template->replace( - $expression->toString(), - $expression->expand($variables), - ); - }, - ); + return self::attempt($template)->maybe(); + } - return Url::of($url->toString()); + public function expansion(): Expansion + { + return Expansion::of($this->template, $this->expressions); } /** - * @throws ExplodeExpressionCantBeMatched + * The attempt will fail when trying to match on a template containing an + * explode expression. * - * @return Map + * @return Attempt> */ - public function extract(Url $url): Map + public function extract(Url $url): Attempt { - /** @var Map */ - return Str::of($url->toString()) - ->capture($this->regex()) - ->filter(static fn($key) => \is_string($key)) - ->map(static fn($_, $variable) => \rawurldecode($variable->toString())); + /** @var Attempt> */ + return $this + ->regex() + ->map(Str::of($url->toString())->capture(...)) + ->map( + static fn($captured) => $captured + ->filter(static fn($key) => \is_string($key)) + ->map(static fn($_, $variable) => \rawurldecode($variable->toString())), + ); } /** - * @throws ExplodeExpressionCantBeMatched + * The attempt will fail when trying to match on a template containing an + * explode expression. + * + * @return Attempt */ - public function matches(Url $url): bool + public function matches(Url $url): Attempt { - return Str::of($url->toString())->matches($this->regex()); + return $this->regex()->map( + Str::of($url->toString())->matches(...), + ); } public function toString(): string @@ -108,9 +105,9 @@ public function toString(): string } /** - * @throws ExplodeExpressionCantBeMatched + * @return Attempt */ - private function regex(): string + private function regex(): Attempt { $template = $this ->expressions @@ -125,18 +122,23 @@ private function regex(): string ), ) ->pregQuote(); - $template = $this->expressions->reduce( - $template, - static fn(Str $template, $expression) => $template->replace( - \sprintf( - '__innmind_expression_%s__', - \spl_object_hash($expression), - ), - $expression->regex(), - ), - ); - return $template->prepend('~^')->append('$~')->toString(); + return $this + ->expressions + ->sink($template) + ->attempt( + static fn($template, $expression) => $expression + ->regex() + ->map(static fn($regex) => $template->replace( + \sprintf( + '__innmind_expression_%s__', + \spl_object_hash($expression), + ), + $regex, + )), + ) + ->map(static fn($regex) => $regex->prepend('~^')->append('$~')) + ->map(static fn($regex) => $regex->toString()); } /** @@ -145,9 +147,9 @@ private function regex(): string * Recursively find the expressions as Str::capture doesnt capture all of * them at the same time * - * @return Maybe> + * @return Attempt> */ - private static function parse(Str $template): Maybe + private static function parse(Str $template): Attempt { /** @var Sequence */ $expressions = Sequence::of(); @@ -166,14 +168,12 @@ private static function parse(Str $template): Maybe ); } while (!$captured->empty()); - /** @var Maybe> */ + /** @var Sequence */ + $parsed = Sequence::of(); + return $expressions ->map(Expressions::of(...)) - ->match( - static fn($first, $rest) => Maybe::all($first, ...$rest->toList())->map( - Sequence::of(...), - ), - static fn() => Maybe::just(Sequence::of()), - ); + ->sink($parsed) + ->attempt(static fn($expressions, $expression) => $expression->map($expressions)); } } diff --git a/src/UrlEncode.php b/src/UrlEncode.php index 3ed78f7..9d2357d 100644 --- a/src/UrlEncode.php +++ b/src/UrlEncode.php @@ -5,7 +5,6 @@ use Innmind\Immutable\{ Str, - Sequence, Monoid\Concat, }; @@ -13,38 +12,32 @@ * @psalm-immutable * @internal */ -final class UrlEncode +enum UrlEncode { - /** @var Sequence */ - private Sequence $safeCharacters; + case everything; + case allowReservedCharacters; - public function __construct() + public function encode(string $string): string { - $this->safeCharacters = Sequence::strings(); - } - - public function __invoke(string $string): string - { - if ($this->safeCharacters->empty()) { + if ($this === self::everything) { return \rawurlencode($string); } return Str::of($string) ->split() ->map(static fn($char) => $char->toString()) - ->map($this->encode(...)) + ->map(self::map(...)) ->map(Str::of(...)) - ->fold(new Concat) + ->fold(Concat::monoid) ->toString(); } /** * @psalm-pure */ - public static function allowReservedCharacters(): self + private static function map(string $char): string { - $self = new self; - $self->safeCharacters = Sequence::strings( + $allowed = [ ':', '/', '?', @@ -63,16 +56,12 @@ public static function allowReservedCharacters(): self ',', ';', '=', - ); + ]; - return $self; - } + if (\in_array($char, $allowed, true)) { + return $char; + } - private function encode(string $char): string - { - return match ($this->safeCharacters->contains($char)) { - true => $char, - false => \rawurlencode($char), - }; + return \rawurlencode($char); } } diff --git a/tests/Expression/Level1Test.php b/tests/Expression/Level1Test.php index 4c12ec9..f6e7e10 100644 --- a/tests/Expression/Level1Test.php +++ b/tests/Expression/Level1Test.php @@ -46,12 +46,18 @@ public function testExpand() $this->assertSame('value', $expression->expand( Map::of(['foo', 'value']), + Map::of(), + Map::of(), )); $this->assertSame('Hello%20World%21', $expression->expand( Map::of(['foo', 'Hello World!']), + Map::of(), + Map::of(), )); $this->assertSame('', $expression->expand( Map::of(), + Map::of(), + Map::of(), )); } @@ -80,7 +86,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)', Level1::of(Str::of('{foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); @@ -94,7 +100,9 @@ public function testReturnEmptyStringWhenTryingToExpandWithAnArray() ); $this->assertSame('', $expression->expand( + Map::of(), Map::of(['foo', ['value']]), + Map::of(), )); } } diff --git a/tests/Expression/Level2/FragmentTest.php b/tests/Expression/Level2/FragmentTest.php index 0fb4c85..0ed4d2b 100644 --- a/tests/Expression/Level2/FragmentTest.php +++ b/tests/Expression/Level2/FragmentTest.php @@ -46,15 +46,23 @@ public function testExpand() $this->assertSame('#value', $expression->expand( Map::of(['foo', 'value']), + Map::of(), + Map::of(), )); $this->assertSame('#Hello%20World!', $expression->expand( Map::of(['foo', 'Hello World!']), + Map::of(), + Map::of(), )); $this->assertSame('#/foo/bar', $expression->expand( Map::of(['foo', '/foo/bar']), + Map::of(), + Map::of(), )); $this->assertSame('', $expression->expand( Map::of(), + Map::of(), + Map::of(), )); } @@ -83,7 +91,7 @@ public function testRegex() $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Fragment::of(Str::of('{#foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); @@ -97,7 +105,9 @@ public function testReturnEmptyStringWhenTryingToExpandWithAnArray() ); $this->assertSame('', $expression->expand( + Map::of(), Map::of(['foo', ['value']]), + Map::of(), )); } } diff --git a/tests/Expression/Level2/ReservedTest.php b/tests/Expression/Level2/ReservedTest.php index f894341..aaa4be6 100644 --- a/tests/Expression/Level2/ReservedTest.php +++ b/tests/Expression/Level2/ReservedTest.php @@ -46,15 +46,23 @@ public function testExpand() $this->assertSame('value', $expression->expand( Map::of(['foo', 'value']), + Map::of(), + Map::of(), )); $this->assertSame('Hello%20World!', $expression->expand( Map::of(['foo', 'Hello World!']), + Map::of(), + Map::of(), )); $this->assertSame('/foo/bar', $expression->expand( Map::of(['foo', '/foo/bar']), + Map::of(), + Map::of(), )); $this->assertSame('', $expression->expand( Map::of(), + Map::of(), + Map::of(), )); } @@ -83,7 +91,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Reserved::of(Str::of('{+foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); @@ -97,7 +105,9 @@ public function testReturnEmptyStringWhenTryingToExpandWithAnArray() ); $this->assertSame('', $expression->expand( + Map::of(), Map::of(['foo', ['value']]), + Map::of(), )); } } diff --git a/tests/Expression/Level3/FragmentTest.php b/tests/Expression/Level3/FragmentTest.php index fd0d27d..c7ce356 100644 --- a/tests/Expression/Level3/FragmentTest.php +++ b/tests/Expression/Level3/FragmentTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '#1024,Hello%20World!,768', Fragment::of(Str::of('{#x,hello,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '#/foo/bar,1024', Fragment::of(Str::of('{#path,x}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*),(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Fragment::of(Str::of('{#foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/LabelTest.php b/tests/Expression/Level3/LabelTest.php index 45e886a..959ee3b 100644 --- a/tests/Expression/Level3/LabelTest.php +++ b/tests/Expression/Level3/LabelTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '.1024.768', Label::of(Str::of('{.x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '.value', Label::of(Str::of('{.var}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\.(?[a-zA-Z0-9\%\-\_\~]*).(?[a-zA-Z0-9\%\-\_\~]*)', Label::of(Str::of('{.foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/ParametersTest.php b/tests/Expression/Level3/ParametersTest.php index 617675a..f1b6036 100644 --- a/tests/Expression/Level3/ParametersTest.php +++ b/tests/Expression/Level3/ParametersTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( ';x=1024;y=768', Parameters::of(Str::of('{;x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( ';x=1024;y=768;empty', Parameters::of(Str::of('{;x,y,empty}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\;foo=?(?[a-zA-Z0-9\%\-\.\_\~]*)\;bar=?(?[a-zA-Z0-9\%\-\.\_\~]*)', Parameters::of(Str::of('{;foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/PathTest.php b/tests/Expression/Level3/PathTest.php index ad42b76..014fbf8 100644 --- a/tests/Expression/Level3/PathTest.php +++ b/tests/Expression/Level3/PathTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '/value', Path::of(Str::of('{/var}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '/value/1024', Path::of(Str::of('{/var,x}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '/(?[a-zA-Z0-9\%\-\.\_\~]*)/(?[a-zA-Z0-9\%\-\.\_\~]*)', Path::of(Str::of('{/foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/QueryContinuationTest.php b/tests/Expression/Level3/QueryContinuationTest.php index 9ecf693..461ec75 100644 --- a/tests/Expression/Level3/QueryContinuationTest.php +++ b/tests/Expression/Level3/QueryContinuationTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '&x=1024&y=768', QueryContinuation::of(Str::of('{&x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '&x=1024&y=768&empty=', QueryContinuation::of(Str::of('{&x,y,empty}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\&foo=(?[a-zA-Z0-9\%\-\.\_\~]*)\&bar=(?[a-zA-Z0-9\%\-\.\_\~]*)', QueryContinuation::of(Str::of('{&foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/QueryTest.php b/tests/Expression/Level3/QueryTest.php index 37d487f..9cf1d51 100644 --- a/tests/Expression/Level3/QueryTest.php +++ b/tests/Expression/Level3/QueryTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '?x=1024&y=768', Query::of(Str::of('{?x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '?x=1024&y=768&empty=', Query::of(Str::of('{?x,y,empty}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '\?foo=(?[a-zA-Z0-9\%\-\.\_\~]*)\&bar=(?[a-zA-Z0-9\%\-\.\_\~]*)', Query::of(Str::of('{?foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3/ReservedTest.php b/tests/Expression/Level3/ReservedTest.php index c65353d..a46bf09 100644 --- a/tests/Expression/Level3/ReservedTest.php +++ b/tests/Expression/Level3/ReservedTest.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '1024,Hello%20World!,768', Reserved::of(Str::of('{+x,hello,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '/foo/bar,1024', Reserved::of(Str::of('{+path,x}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*),(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Reserved::of(Str::of('{+foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level3Test.php b/tests/Expression/Level3Test.php index 4cf0344..bf2f5e3 100644 --- a/tests/Expression/Level3Test.php +++ b/tests/Expression/Level3Test.php @@ -50,14 +50,14 @@ public function testExpand() $this->assertSame( '1024,768', Level3::of(Str::of('{x,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); $this->assertSame( '1024,Hello%20World%21,768', Level3::of(Str::of('{x,hello,y}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($variables, Map::of(), Map::of()), static fn() => null, ), ); @@ -88,7 +88,7 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*),(?[a-zA-Z0-9\%\-\.\_\~]*)', Level3::of(Str::of('{foo,bar}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/CompositeTest.php b/tests/Expression/Level4/CompositeTest.php index 6ff72f1..495beb1 100644 --- a/tests/Expression/Level4/CompositeTest.php +++ b/tests/Expression/Level4/CompositeTest.php @@ -47,24 +47,26 @@ public function testStringCast() public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $this->assertSame( '/v/value', Composite::of(Str::of('{/var:1,var}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/red/green/blue/%2Ffoo', Composite::of(Str::of('{/list*,path:4}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -73,11 +75,13 @@ public function testExpand() #[DataProvider('cases')] public function testOf($pattern, $expected) { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); $expression = Composite::of(Str::of($pattern))->match( @@ -86,7 +90,7 @@ public function testOf($pattern, $expected) ); $this->assertSame($pattern, $expression->toString()); - $this->assertSame($expected, $expression->expand($variables)); + $this->assertSame($expected, $expression->expand($values, $lists, $keys)); } public function testReturnNothingWhenInvalidPattern() @@ -102,63 +106,63 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)\,(?[a-zA-Z0-9\%\-\.\_\~]*)', Composite::of(Str::of('{var,hello}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)\,(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)\,(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{5})', Composite::of(Str::of('{+var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)\,(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{5})', Composite::of(Str::of('{#var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\.(?[a-zA-Z0-9\%\-\.\_\~]*)\.(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{.var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\/(?[a-zA-Z0-9\%\-\.\_\~]*)\/(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{/var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\;var=(?[a-zA-Z0-9\%\-\.\_\~]*)\;hello=(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{;var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\?var=(?[a-zA-Z0-9\%\-\.\_\~]*)\&hello=(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{?var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\&var=(?[a-zA-Z0-9\%\-\.\_\~]*)\&hello=(?[a-zA-Z0-9\%\-\.\_\~]{5})', Composite::of(Str::of('{&var,hello:5}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/FragmentTest.php b/tests/Expression/Level4/FragmentTest.php index fb752f5..dff6292 100644 --- a/tests/Expression/Level4/FragmentTest.php +++ b/tests/Expression/Level4/FragmentTest.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4\Fragment, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,45 +86,51 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '#/foo/b', Fragment::of(Str::of('{#path:6}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#red,green,blue', Fragment::of(Str::of('{#list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#red,green,blue', Fragment::of(Str::of('{#list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#semi,;,dot,.,comma,,', Fragment::of(Str::of('{#keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '#semi=;,dot=.,comma=,', Fragment::of(Str::of('{#keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -168,10 +174,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - Fragment::of(Str::of('{#foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = Fragment::of(Str::of('{#foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -181,14 +187,14 @@ public function testRegex() $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Fragment::of(Str::of('{#foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\#(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{2})', Fragment::of(Str::of('{#foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/LabelTest.php b/tests/Expression/Level4/LabelTest.php index 9ed0645..4f72d79 100644 --- a/tests/Expression/Level4/LabelTest.php +++ b/tests/Expression/Level4/LabelTest.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4\Label, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,45 +86,51 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '.val', Label::of(Str::of('{.var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.red,green,blue', Label::of(Str::of('{.list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.red.green.blue', Label::of(Str::of('{.list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.semi,%3B,dot,.,comma,%2C', Label::of(Str::of('{.keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '.semi=%3B.dot=..comma=%2C', Label::of(Str::of('{.keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -168,10 +174,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - Label::of(Str::of('{.foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = Label::of(Str::of('{.foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -181,14 +187,14 @@ public function testRegex() $this->assertSame( '\.(?[a-zA-Z0-9\%\-\.\_\~]*)', Label::of(Str::of('{.foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\.(?[a-zA-Z0-9\%\-\.\_\~]{2})', Label::of(Str::of('{.foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/ParametersTest.php b/tests/Expression/Level4/ParametersTest.php index 3541c1b..13a036d 100644 --- a/tests/Expression/Level4/ParametersTest.php +++ b/tests/Expression/Level4/ParametersTest.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4\Parameters, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,45 +86,51 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( ';hello=Hello', Parameters::of(Str::of('{;hello:5}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';list=red,green,blue', Parameters::of(Str::of('{;list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';list=red;list=green;list=blue', Parameters::of(Str::of('{;list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';keys=semi,%3B,dot,.,comma,%2C', Parameters::of(Str::of('{;keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( ';semi=%3B;dot=.;comma=%2C', Parameters::of(Str::of('{;keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -168,10 +174,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - Parameters::of(Str::of('{;foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = Parameters::of(Str::of('{;foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -181,14 +187,14 @@ public function testRegex() $this->assertSame( '\;foo=(?[a-zA-Z0-9\%\-\.\_\~]*)', Parameters::of(Str::of('{;foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\;foo=(?[a-zA-Z0-9\%\-\.\_\~]{2})', Parameters::of(Str::of('{;foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/PathTest.php b/tests/Expression/Level4/PathTest.php index 0aaf2dc..9ff0abb 100644 --- a/tests/Expression/Level4/PathTest.php +++ b/tests/Expression/Level4/PathTest.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4\Path, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,38 +86,44 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '/red,green,blue', Path::of(Str::of('{/list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/red/green/blue', Path::of(Str::of('{/list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/semi,%3B,dot,.,comma,%2C', Path::of(Str::of('{/keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '/semi=%3B/dot=./comma=%2C', Path::of(Str::of('{/keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -161,10 +167,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - Path::of(Str::of('{/foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = Path::of(Str::of('{/foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -174,14 +180,14 @@ public function testRegex() $this->assertSame( '\/(?[a-zA-Z0-9\%\-\.\_\~]*)', Path::of(Str::of('{/foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\/(?[a-zA-Z0-9\%\-\.\_\~]{2})', Path::of(Str::of('{/foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/QueryContinuationTest.php b/tests/Expression/Level4/QueryContinuationTest.php index 6300a0d..3aeae63 100644 --- a/tests/Expression/Level4/QueryContinuationTest.php +++ b/tests/Expression/Level4/QueryContinuationTest.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4\QueryContinuation, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,45 +86,51 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '&var=val', QueryContinuation::of(Str::of('{&var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&list=red,green,blue', QueryContinuation::of(Str::of('{&list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&list=red&list=green&list=blue', QueryContinuation::of(Str::of('{&list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&keys=semi,%3B,dot,.,comma,%2C', QueryContinuation::of(Str::of('{&keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '&semi=%3B&dot=.&comma=%2C', QueryContinuation::of(Str::of('{&keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -168,10 +174,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - QueryContinuation::of(Str::of('{&foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = QueryContinuation::of(Str::of('{&foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -181,14 +187,14 @@ public function testRegex() $this->assertSame( '\&foo=(?[a-zA-Z0-9\%\-\.\_\~]*)', QueryContinuation::of(Str::of('{&foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\&foo=(?[a-zA-Z0-9\%\-\.\_\~]{2})', QueryContinuation::of(Str::of('{&foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/QueryTest.php b/tests/Expression/Level4/QueryTest.php index b95b0cf..6044c35 100644 --- a/tests/Expression/Level4/QueryTest.php +++ b/tests/Expression/Level4/QueryTest.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4\Query, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,45 +86,51 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '?var=val', Query::of(Str::of('{?var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?list=red,green,blue', Query::of(Str::of('{?list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?list=red&list=green&list=blue', Query::of(Str::of('{?list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?keys=semi,%3B,dot,.,comma,%2C', Query::of(Str::of('{?keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '?semi=%3B&dot=.&comma=%2C', Query::of(Str::of('{?keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -168,10 +174,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - Query::of(Str::of('{?foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = Query::of(Str::of('{?foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -181,14 +187,14 @@ public function testRegex() $this->assertSame( '\?foo=(?[a-zA-Z0-9\%\-\.\_\~]*)', Query::of(Str::of('{?foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '\?foo=(?[a-zA-Z0-9\%\-\.\_\~]{2})', Query::of(Str::of('{?foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4/ReservedTest.php b/tests/Expression/Level4/ReservedTest.php index 2b13056..3fc8032 100644 --- a/tests/Expression/Level4/ReservedTest.php +++ b/tests/Expression/Level4/ReservedTest.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4\Reserved, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,45 +86,51 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( '/foo/b', Reserved::of(Str::of('{+path:6}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Reserved::of(Str::of('{+list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Reserved::of(Str::of('{+list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi,;,dot,.,comma,,', Reserved::of(Str::of('{+keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi=;,dot=.,comma=,', Reserved::of(Str::of('{+keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -168,10 +174,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - Reserved::of(Str::of('{+foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = Reserved::of(Str::of('{+foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -181,14 +187,14 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]*)', Reserved::of(Str::of('{+foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%:/\?#\[\]@!$&\'\(\)\*\+,;=\-\.\_\~]{2})', Reserved::of(Str::of('{+foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/Level4Test.php b/tests/Expression/Level4Test.php index 68b5571..8b4cd65 100644 --- a/tests/Expression/Level4Test.php +++ b/tests/Expression/Level4Test.php @@ -5,8 +5,8 @@ use Innmind\UrlTemplate\{ Expression\Level4, + Expression\Name, Expression, - Exception\LogicException, }; use Innmind\Immutable\{ Map, @@ -86,59 +86,65 @@ public function testReturnNothingWhenNegativeLimit(): BlackBox\Proof public function testExpand() { - $variables = Map::of() + $values = Map::of() ('var', 'value') ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]); + ('path', '/foo/bar'); + $lists = Map::of() + ('list', ['red', 'green', 'blue']); + $keys = Map::of() + ('keys', [ + [Name::of('semi'), ';'], + [Name::of('dot'), '.'], + [Name::of('comma'), ','], + ]); $this->assertSame( 'val', Level4::of(Str::of('{var:3}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'value', Level4::of(Str::of('{var:30}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( '%2Ffoo', Level4::of(Str::of('{path:4}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Level4::of(Str::of('{list}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'red,green,blue', Level4::of(Str::of('{list*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi,%3B,dot,.,comma,%2C', Level4::of(Str::of('{keys}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); $this->assertSame( 'semi=%3B,dot=.,comma=%2C', Level4::of(Str::of('{keys*}'))->match( - static fn($expression) => $expression->expand($variables), + static fn($expression) => $expression->expand($values, $lists, $keys), static fn() => null, ), ); @@ -182,10 +188,10 @@ public function testReturnNothingWhenInvalidPattern() public function testThrowExplodeRegex() { - $this->expectException(LogicException::class); + $this->expectException(\LogicException::class); - Level4::of(Str::of('{foo*}'))->match( - static fn($expression) => $expression->regex(), + $_ = Level4::of(Str::of('{foo*}'))->match( + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ); } @@ -195,14 +201,14 @@ public function testRegex() $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]*)', Level4::of(Str::of('{foo}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); $this->assertSame( '(?[a-zA-Z0-9\%\-\.\_\~]{2})', Level4::of(Str::of('{foo:2}'))->match( - static fn($expression) => $expression->regex(), + static fn($expression) => $expression->regex()->unwrap(), static fn() => null, ), ); diff --git a/tests/Expression/NameTest.php b/tests/Expression/NameTest.php index 9fa9081..b69c2c2 100644 --- a/tests/Expression/NameTest.php +++ b/tests/Expression/NameTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\UrlTemplate\Expression; -use Innmind\UrlTemplate\{ - Expression\Name, - Exception\DomainException, -}; +use Innmind\UrlTemplate\Expression\Name; use Innmind\BlackBox\{ PHPUnit\BlackBox, PHPUnit\Framework\TestCase, @@ -44,7 +41,7 @@ public function testThrowWhenInvalidName(): BlackBox\Proof }), ) ->prove(function(string $string): void { - $this->expectException(DomainException::class); + $this->expectException(\DomainException::class); $this->expectExceptionMessage($string); Name::of($string); diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index e8dc56e..b902e27 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\UrlTemplate; -use Innmind\UrlTemplate\{ - Template, - Exception\ExplodeExpressionCantBeMatched, -}; +use Innmind\UrlTemplate\Template; use Innmind\Url\Url; use Innmind\Immutable\Map; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -32,22 +29,22 @@ public function testOf() #[DataProvider('cases')] public function testExpand($pattern, $expected) { - $variables = Map::of() - ('var', 'value') - ('hello', 'Hello World!') - ('path', '/foo/bar') - ('list', ['red', 'green', 'blue']) - ('keys', [['semi', ';'], ['dot', '.'], ['comma', ',']]) - ('username', 'fred') - ('term', 'dog') - ('q', 'chien') - ('lang', 'fr') - ('x', '1024') - ('y', '768'); - $template = Template::of($pattern); - $url = $template->expand($variables); + $url = $template + ->expansion() + ->with('var', 'value') + ->with('hello', 'Hello World!') + ->with('path', '/foo/bar') + ->with('list', 'red', 'green', 'blue') + ->withKeys('keys', ['semi', ';'], ['dot', '.'], ['comma', ',']) + ->with('username', 'fred') + ->with('term', 'dog') + ->with('q', 'chien') + ->with('lang', 'fr') + ->with('x', '1024') + ->with('y', '768') + ->expand(); $this->assertInstanceOf(Url::class, $url); $this->assertSame($expected, $url->toString()); @@ -55,18 +52,23 @@ public function testExpand($pattern, $expected) public function testReturnEmptyMapWhenUrlDoesntMatchTemplate() { - $this->assertCount( + $this->assertSame( 0, - Template::of('/{foo}')->extract(Url::of('/hello%20world%21/foo')), + Template::of('/{foo}') + ->extract(Url::of('/hello%20world%21/foo')) + ->unwrap() + ->size(), ); } public function testLevel1Extraction() { - $variables = Template::of('/{foo}/{bar}')->extract(Url::of('/hello%20world%21/foo')); + $variables = Template::of('/{foo}/{bar}') + ->extract(Url::of('/hello%20world%21/foo')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('hello world!', $variables->get('foo')->match( static fn($value) => $value, static fn() => null, @@ -79,19 +81,23 @@ public function testLevel1Extraction() public function testLevel2Extraction() { - $variables = Template::of('{+path}/here')->extract(Url::of('/foo/bar/here')); + $variables = Template::of('{+path}/here') + ->extract(Url::of('/foo/bar/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('/foo/bar', $variables->get('path')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('X{#hello}')->extract(Url::of('X#Hello%20World!')); + $variables = Template::of('X{#hello}') + ->extract(Url::of('X#Hello%20World!')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('Hello World!', $variables->get('hello')->match( static fn($value) => $value, static fn() => null, @@ -100,10 +106,12 @@ public function testLevel2Extraction() public function testLevel3Extraction() { - $variables = Template::of('/map?{x,y}')->extract(Url::of('/map?1024,768')); + $variables = Template::of('/map?{x,y}') + ->extract(Url::of('/map?1024,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -113,10 +121,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('/{x,hello,y}')->extract(Url::of('/1024,Hello%20World%21,768')); + $variables = Template::of('/{x,hello,y}') + ->extract(Url::of('/1024,Hello%20World%21,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -130,10 +140,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('/{+x,hello,y}')->extract(Url::of('/1024,Hello%20World!,768')); + $variables = Template::of('/{+x,hello,y}') + ->extract(Url::of('/1024,Hello%20World!,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -147,10 +159,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{+path,x}/here')->extract(Url::of('/foo/bar,1024/here')); + $variables = Template::of('{+path,x}/here') + ->extract(Url::of('/foo/bar,1024/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -160,10 +174,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{#x,hello,y}')->extract(Url::of('#1024,Hello%20World!,768')); + $variables = Template::of('{#x,hello,y}') + ->extract(Url::of('#1024,Hello%20World!,768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -177,10 +193,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{#path,x}/here')->extract(Url::of('#/foo/bar,1024/here')); + $variables = Template::of('{#path,x}/here') + ->extract(Url::of('#/foo/bar,1024/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -190,10 +208,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{.x,y}')->extract(Url::of('.1024.768')); + $variables = Template::of('{.x,y}') + ->extract(Url::of('.1024.768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -203,10 +223,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{/var,x}/here')->extract(Url::of('/value/1024/here')); + $variables = Template::of('{/var,x}/here') + ->extract(Url::of('/value/1024/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('value', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -216,10 +238,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{;x,y}')->extract(Url::of(';x=1024;y=768')); + $variables = Template::of('{;x,y}') + ->extract(Url::of(';x=1024;y=768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -229,10 +253,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{;x,y,empty}')->extract(Url::of(';x=1024;y=768;empty')); + $variables = Template::of('{;x,y,empty}') + ->extract(Url::of(';x=1024;y=768;empty')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -246,10 +272,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{?x,y}')->extract(Url::of('?x=1024&y=768')); + $variables = Template::of('{?x,y}') + ->extract(Url::of('?x=1024&y=768')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -259,10 +287,12 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('{?x,y,empty}')->extract(Url::of('?x=1024&y=768&empty=')); + $variables = Template::of('{?x,y,empty}') + ->extract(Url::of('?x=1024&y=768&empty=')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -276,19 +306,23 @@ public function testLevel3Extraction() static fn() => null, )); - $variables = Template::of('?fixed=yes{&x}')->extract(Url::of('?fixed=yes&x=1024')); + $variables = Template::of('?fixed=yes{&x}') + ->extract(Url::of('?fixed=yes&x=1024')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{&x,y,empty}')->extract(Url::of('&x=1024&y=768&empty=')); + $variables = Template::of('{&x,y,empty}') + ->extract(Url::of('&x=1024&y=768&empty=')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(3, $variables); + $this->assertSame(3, $variables->size()); $this->assertSame('1024', $variables->get('x')->match( static fn($value) => $value, static fn() => null, @@ -305,73 +339,89 @@ public function testLevel3Extraction() public function testLevel4Extraction() { - $variables = Template::of('{var:3}')->extract(Url::of('val')); + $variables = Template::of('{var:3}') + ->extract(Url::of('val')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('val', $variables->get('var')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{+path:6}/here')->extract(Url::of('/foo/b/here')); + $variables = Template::of('{+path:6}/here') + ->extract(Url::of('/foo/b/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('/foo/b', $variables->get('path')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{#path:6}/here')->extract(Url::of('#/foo/b/here')); + $variables = Template::of('{#path:6}/here') + ->extract(Url::of('#/foo/b/here')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('/foo/b', $variables->get('path')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{.var:3}')->extract(Url::of('.val')); + $variables = Template::of('{.var:3}') + ->extract(Url::of('.val')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('val', $variables->get('var')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{/var:1}')->extract(Url::of('/v')); + $variables = Template::of('{/var:1}') + ->extract(Url::of('/v')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('v', $variables->get('var')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{;var:5}')->extract(Url::of(';var=hello')); + $variables = Template::of('{;var:5}') + ->extract(Url::of(';var=hello')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('hello', $variables->get('var')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{?var:3}')->extract(Url::of('?var=hel')); + $variables = Template::of('{?var:3}') + ->extract(Url::of('?var=hel')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('hel', $variables->get('var')->match( static fn($value) => $value, static fn() => null, )); - $variables = Template::of('{&var:3}')->extract(Url::of('&var=hel')); + $variables = Template::of('{&var:3}') + ->extract(Url::of('&var=hel')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(1, $variables); + $this->assertSame(1, $variables->size()); $this->assertSame('hel', $variables->get('var')->match( static fn($value) => $value, static fn() => null, @@ -381,10 +431,11 @@ public function testLevel4Extraction() public function testExtraction() { $variables = Template::of('http://example.com/search{?q,lang:2}') - ->extract(Url::of('http://example.com/search?q=chien&lang=fr')); + ->extract(Url::of('http://example.com/search?q=chien&lang=fr')) + ->unwrap(); $this->assertInstanceOf(Map::class, $variables); - $this->assertCount(2, $variables); + $this->assertSame(2, $variables->size()); $this->assertSame('chien', $variables->get('q')->match( static fn($value) => $value, static fn() => null, @@ -397,25 +448,27 @@ public function testExtraction() public function testThrowWhenExtractionNotSupportedForTemplate() { - $this->expectException(ExplodeExpressionCantBeMatched::class); + $this->expectException(\LogicException::class); - Template::of('{foo*}')->extract(Url::of('foo,bar,baz')); + $_ = Template::of('{foo*}') + ->extract(Url::of('foo,bar,baz')) + ->unwrap(); } public function testMatches() { $template = Template::of('{/foo}'); - $this->assertTrue($template->matches(Url::of('/bar'))); - $this->assertFalse($template->matches(Url::of('/bar/foo'))); + $this->assertTrue($template->matches(Url::of('/bar'))->unwrap()); + $this->assertFalse($template->matches(Url::of('/bar/foo'))->unwrap()); } public function testNoNeedToEscapeSpecialRegexCharactersInTheUrl() { $template = Template::of('/*'); - $this->assertTrue($template->matches(Url::of('/*'))); - $this->assertFalse($template->matches(Url::of('/f'))); + $this->assertTrue($template->matches(Url::of('/*'))->unwrap()); + $this->assertFalse($template->matches(Url::of('/f'))->unwrap()); } public static function cases(): array diff --git a/tests/UrlEncodeTest.php b/tests/UrlEncodeTest.php index 57c2df6..db47411 100644 --- a/tests/UrlEncodeTest.php +++ b/tests/UrlEncodeTest.php @@ -19,9 +19,9 @@ public function testStandardEncode(): BlackBox\Proof return $this ->forAll(Set::strings()) ->prove(function(string $string): void { - $encode = new UrlEncode; + $encode = UrlEncode::everything; - $this->assertSame(\rawurlencode($string), $encode($string)); + $this->assertSame(\rawurlencode($string), $encode->encode($string)); }); } @@ -49,24 +49,24 @@ public function testSafeCharactersAreNotEncoded(): BlackBox\Proof '=', )) ->prove(function(string $char): void { - $encode = UrlEncode::allowReservedCharacters(); + $encode = UrlEncode::allowReservedCharacters; - $this->assertSame($char, $encode($char)); + $this->assertSame($char, $encode->encode($char)); }); } public function testSafeCharactersAreNotEncodedEvenWhenInMiddleOfString() { - $encode = UrlEncode::allowReservedCharacters(); + $encode = UrlEncode::allowReservedCharacters; $this->assertSame( ':%20)', - $encode(': )'), + $encode->encode(': )'), ); } public function testDoesNothingOnEmptyString() { - $this->assertSame('', UrlEncode::allowReservedCharacters()('')); + $this->assertSame('', UrlEncode::allowReservedCharacters->encode('')); } }