Skip to content

Commit

Permalink
remove final and adding dedicated exception classes
Browse files Browse the repository at this point in the history
  • Loading branch information
yceruto committed Jul 12, 2024
1 parent c54745a commit 66bd8cd
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
$header = <<<EOF
This file is part of Option Type package.
(c) Yonel Ceruto <patch@yceruto.dev>
(c) Yonel Ceruto <open@yceruto.dev>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"authors": [
{
"name": "Yonel Ceruto",
"email": "patch@yceruto.dev"
"email": "open@yceruto.dev"
}
],
"autoload": {
Expand All @@ -25,7 +25,7 @@
},
"require-dev": {
"phpunit/phpunit": "^11.1",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan": "^1.11",
"friendsofphp/php-cs-fixer": "^3.54"
}
}
16 changes: 8 additions & 8 deletions docs/api_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ _Table of Contents:_

### **`Option::some(mixed $value): self`**

Creates an `Option` instance containing a non-null value. If `null` is passed, a `LogicException` is thrown.
Creates an `Option` instance containing a non-null value. If `null` is passed, a `LogicOptionException` is thrown.

```php
$opt = Option::some(10);
echo $opt->unwrap(); // Outputs: 10

Option::some(null); // Throws LogicException
Option::some(null); // Throws LogicOptionException
```

### **`Option::none(): self`**
Expand Down Expand Up @@ -107,26 +107,26 @@ echo $opt->isNone(); // Outputs: true

### **`Option::expect(string $message): mixed`**

Returns the contained value if it is `Some`; otherwise, throws a `LogicException` with a custom message.
Returns the contained value if it is `Some`; otherwise, throws a `RuntimeOptionException` with a custom message.

```php
$opt = Option::some(10);
echo $opt->expect('A number.'); // Outputs: 10

$opt = Option::none();
echo $opt->expect('A number.'); // Throws LogicException with custom message
echo $opt->expect('A number.'); // Throws RuntimeOptionException with custom message
```

### **`Option::unwrap(): mixed`**

Returns the contained value if it is `Some`; otherwise, throws a `LogicException`.
Returns the contained value if it is `Some`; otherwise, throws a `RuntimeOptionException`.

```php
$opt = Option::some(10);
echo $opt->unwrap(); // Outputs: 10

$opt = Option::none();
echo $opt->unwrap(); // Throws LogicException
echo $opt->unwrap(); // Throws RuntimeOptionException
```

### **`Option::unwrapOr(mixed $default): mixed`**
Expand Down Expand Up @@ -356,13 +356,13 @@ echo $x->equals($y); // Outputs: true

### **`some(mixed $value): Option`**

Creates an `Option` instance containing a non-null value. If `null` is passed, a `LogicException` is thrown.
Creates an `Option` instance containing a non-null value. If `null` is passed, a `LogicOptionException` is thrown.

```php
$opt = some(10); // Same as Option::some(10)
echo $opt->unwrap(); // Outputs: 10

some(null); // Throws LogicException
some(null); // Throws LogicOptionException
```

### **`none(): Option`**
Expand Down
2 changes: 1 addition & 1 deletion docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function findUser(int $id): Option
return Option::from($user); // None if null, otherwise Some($user)
}

$user = findUser(1)->expect('the user exists.'); // throws LogicException if it does not exist
$user = findUser(1)->expect('A user must exist!'); // throws LogicOptionException if it does not exist
// do something safely with $user instance...

// map the user found to a DTO or create a new one if not found
Expand Down
18 changes: 18 additions & 0 deletions src/Exception/LogicOptionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

/*
* This file is part of Option Type package.
*
* (c) Yonel Ceruto <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Std\Type\Exception;

class LogicOptionException extends \LogicException
{
}
18 changes: 18 additions & 0 deletions src/Exception/RuntimeOptionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

/*
* This file is part of Option Type package.
*
* (c) Yonel Ceruto <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Std\Type\Exception;

class RuntimeOptionException extends \RuntimeException
{
}
65 changes: 34 additions & 31 deletions src/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
/*
* This file is part of Option Type package.
*
* (c) Yonel Ceruto <patch@yceruto.dev>
* (c) Yonel Ceruto <open@yceruto.dev>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Std\Type;

use Std\Type\Exception\LogicOptionException;
use Std\Type\Exception\RuntimeOptionException;

use function Std\Type\Option\some;

/**
Expand All @@ -21,7 +24,7 @@
*
* @template T
*/
final readonly class Option
readonly class Option
{
/**
* Some value.
Expand All @@ -30,27 +33,27 @@
*
* @param T $value A value of type T
*
* @return self<T> Some Option
* @return static Some Option
*/
public static function some(mixed $value): self
public static function some(mixed $value): static
{
if (null === $value) {
throw new \LogicException('Cannot create a Some option with a null value, use None instead.');
throw new LogicOptionException(sprintf('Cannot create %s() option with a null value, use %s() instead.', static::class.'::some', static::class.'::none'));
}

return new self($value);
return new static($value);
}

/**
* No value.
*
* Also see {@see none()} for a shorter way to create a Some Option.
*
* @return self<null> None Option
* @return static None Option
*/
public static function none(): self
public static function none(): static
{
return new self(null);
return new static(null);
}

/**
Expand All @@ -69,11 +72,11 @@ public static function none(): self
*
* @param T $value A value of type T
*
* @return self<T>|self<null> Some or None Option
* @return static Some or None Option
*/
public static function from(mixed $value): self
public static function from(mixed $value): static
{
return null === $value ? self::none() : self::some($value);
return null === $value ? static::none() : static::some($value);
}

/**
Expand All @@ -96,7 +99,7 @@ public function isSome(): bool
}

/**
* Returns `true` if the option is a {@see none} value.
* Returns `true` if the option is {@see none} value.
*
* <b>Examples</b>
* ```
Expand All @@ -107,7 +110,7 @@ public function isSome(): bool
* assert($x->isNone(), 'Expected $x to be None.');
* ```
*
* @return bool `true` if the option is a {@see none} value, otherwise `false`
* @return bool `true` if the option is {@see none} value, otherwise `false`
*/
public function isNone(): bool
{
Expand All @@ -132,54 +135,54 @@ public function match(callable $some, callable $none): mixed
}

/**
* Returns the contained {@see some} value, or throws an exception with custom message if the value is a {@see none}.
* Returns the contained {@see some} value, or throws an exception with custom message if the value is {@see none}.
*
* <b>Examples</b>
* ```
* $x = some(2);
* assert(2 === $x->expect('A number.'), 'Expected $x to be 2.');
* assert(2 === $x->expect('A number must be provided.'), 'Expected $x to be 2.');
*
* $x = none();
* $x->expect('A number.'); // throws LogicException
* $x->expect('A number.'); // throws RuntimeOptionException
* ```
*
* @param string $message A custom error message to use in the LogicException
* @param string $message A custom error message to use in the RuntimeOptionException
*
* @return T The contained value
*
* @throws \LogicException If the value is a {@see none} with a custom error message provided.
* We recommend that `expect()` messages are used to describe the reason
* you expect the `Option` should be {@see some}.
* @throws RuntimeOptionException If the value is {@see none} with a custom error message provided.
* We recommend that `expect()` messages are used to describe the reason
* you expect the `Option` should be {@see some}.
*/
public function expect(string $message): mixed
{
if ($this->isNone()) {
throw new \LogicException($message);
throw new RuntimeOptionException($message);
}

return $this->value;
}

/**
* Returns the contained {@see some} value, or throws an exception if the value is a {@see none}.
* Returns the contained {@see some} value, or throws an exception if the value is {@see none}.
*
* <b>Examples</b>
* ```
* $x = some(2);
* assert(2 === $x->unwrap(), 'Expected $x to be 2.');
*
* $x = none();
* $x->unwrap(); // throws LogicException
* $x->unwrap(); // throws LogicOptionException
* ```
*
* @return T The contained value
*
* @throws \LogicException If the value is a {@see none}
* @throws RuntimeOptionException If the value is {@see none}
*/
public function unwrap(): mixed
{
if ($this->isNone()) {
throw new \LogicException('Called Option::unwrap() on a None value.');
throw new RuntimeOptionException(sprintf('Calling %s() method on a None value. Check %s() first or use a fallback method instead.', static::class.'::unwrap', static::class.'::isNone'));
}

return $this->value;
Expand Down Expand Up @@ -240,12 +243,12 @@ public function unwrapOrElse(callable $fn): mixed
* assert(2 === $x->unwrapOrThrow(), 'Expected $x to be 2.');
*
* $x = none();
* $x->unwrapOrThrow(new UnknownNumberError()); // throws LogicException
* $x->unwrapOrThrow(new UnknownNumberError()); // throws UnknownNumberError
* ```
*
* @return T The contained value
*
* @throws \Throwable If the value is a {@see none}
* @throws \Throwable If the value is {@see none}
*/
public function unwrapOrThrow(\Throwable $error): mixed
{
Expand Down Expand Up @@ -573,7 +576,7 @@ public function equals(self $option): bool
*
* @return self<null>|self<T> The flattened Option
*
* @throws \LogicException If the value is not an Option
* @throws LogicOptionException If the value is not an Option
*/
public function flatten(): self
{
Expand All @@ -585,7 +588,7 @@ public function flatten(): self
return $this->value;
}

throw new \LogicException('Cannot flatten a non-Option value.');
throw new LogicOptionException(sprintf('Calling %s() method on a non-Option value. Unexpected "%s" type.', static::class.'::flatten', get_debug_type($this->value)));
}

/**
Expand All @@ -601,7 +604,7 @@ public function clone(): self
/**
* @param T $value A value of type T
*/
private function __construct(
final private function __construct(
private mixed $value,
) {
}
Expand Down
4 changes: 2 additions & 2 deletions src/Option/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/*
* This file is part of Option Type package.
*
* (c) Yonel Ceruto <patch@yceruto.dev>
* (c) Yonel Ceruto <open@yceruto.dev>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
Expand All @@ -27,7 +27,7 @@
*/
function some(mixed $value): Option
{
/** @var Option<T> */
/* @var Option<T> */
return Option::some($value);
}
}
Expand Down
Loading

0 comments on commit 66bd8cd

Please sign in to comment.