Page MenuHomeDevCentral

No OneTemporary

diff --git a/src/DataTypes/Option/None.php b/src/DataTypes/Option/None.php
index 4813b12..dc29e34 100644
--- a/src/DataTypes/Option/None.php
+++ b/src/DataTypes/Option/None.php
@@ -1,32 +1,45 @@
<?php
namespace Keruald\OmniTools\DataTypes\Option;
use InvalidArgumentException;
class None extends Option {
public function isSome () : bool {
return false;
}
public function isNone () : bool {
return true;
}
public function getValue () : mixed {
throw new InvalidArgumentException(<<<'EOD'
This option is a none, so it doesn't have a value.
You can check first with isSome() if this is a value.
EOD
);
}
public function map (callable $callable) : Option {
return $this;
}
- public function orElse (mixed $default) : mixed {
+ public function or (Option $default) : Option {
return $default;
}
+
+ public function orElse (callable $callable) : Option {
+ return $callable();
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $default;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
+ return $callable();
+ }
+
}
diff --git a/src/DataTypes/Option/Option.php b/src/DataTypes/Option/Option.php
index ab97d81..6579395 100644
--- a/src/DataTypes/Option/Option.php
+++ b/src/DataTypes/Option/Option.php
@@ -1,33 +1,66 @@
<?php
namespace Keruald\OmniTools\DataTypes\Option;
abstract class Option {
public abstract function isSome () : bool;
public abstract function isNone () : bool;
public abstract function getValue() : mixed;
public abstract function map(callable $callable) : self;
- public abstract function orElse(mixed $default) : mixed;
+ /**
+ * Returns the option if it contains a value,
+ * otherwise returns the default option.
+ */
+ public abstract function or(Option $default) : self;
+
+ /**
+ * Returns the option if it contains a value,
+ * otherwise calls the callable and returns the result.
+ *
+ * The callable is called only on None, allowing lazy evaluation.
+ * The callable must return an Option.
+ */
+ public abstract function orElse(callable $callable) : self;
+
+ /**
+ * Returns the value of the option if it is Some,
+ * or the default value if the option is None.
+ *
+ * @param mixed $default The default value
+ * @return mixed
+ */
+ public abstract function getValueOr(mixed $default) : mixed;
+
+ /**
+ * Returns the value of the option if it is Some,
+ * or the result of the callable if the option is None.
+ *
+ * The callable is called only on None, allowing lazy evaluation.
+ *
+ * @param callable $callable A function that returns a default value.
+ * @return mixed
+ */
+ public abstract function getValueOrElse(callable $callable) : mixed;
///
/// Helper to build options
///
/**
* Converts a nullable value to an Option.
*
* @param mixed $value
*
* @return Option An instance of None if the value is null; otherwise, an instance of Some.
*/
public static function from (mixed $value) : self {
return match ($value) {
null => new None,
default => new Some($value),
};
}
}
diff --git a/src/DataTypes/Option/Some.php b/src/DataTypes/Option/Some.php
index ab4fbb8..d449d94 100644
--- a/src/DataTypes/Option/Some.php
+++ b/src/DataTypes/Option/Some.php
@@ -1,59 +1,71 @@
<?php
namespace Keruald\OmniTools\DataTypes\Option;
use InvalidArgumentException;
use Keruald\OmniTools\Reflection\Type;
class Some extends Option {
private mixed $value = null;
private string $type = "NULL";
public function __construct ($value = null) {
if ($value !== null) {
$this->setValue($value);
}
}
public function isSome () : bool {
return true;
}
public function isNone () : bool {
return false;
}
public function getValue () : mixed {
return $this->value;
}
public function setValue (mixed $value) : void {
$type = Type::getTypeOf($value);
if (!$this->isAcceptableValueType($type)) {
throw new InvalidArgumentException(<<<'EOD'
When you mutate the value of an Some object, you can't mutate the object type.
Please consider return a new Some instead.
EOD
);
}
$this->value = $value;
$this->type = $type;
}
private function isAcceptableValueType (string $type) : bool {
return $this->value === null || $type === $this->type;
}
public function map (callable $callable) : Option {
$value = $callable($this->value);
return new self($value);
}
- public function orElse (mixed $default) : mixed {
+ public function or (Option $default) : Option {
+ return $this;
+ }
+
+ public function orElse (callable $callable) : Option {
+ return $this;
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $this->value;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
return $this->value;
}
}
diff --git a/src/DataTypes/Result/Err.php b/src/DataTypes/Result/Err.php
index 1661c28..0f62c77 100644
--- a/src/DataTypes/Result/Err.php
+++ b/src/DataTypes/Result/Err.php
@@ -1,54 +1,87 @@
<?php
namespace Keruald\OmniTools\DataTypes\Result;
-use Exception;
+use Keruald\OmniTools\Reflection\CallableElement;
+
use InvalidArgumentException;
use Throwable;
class Err extends Result {
private ?Throwable $error;
+ const CB_TOO_MANY_ARGS = "The callback must take 0 or 1 argument.";
+
public function __construct (Throwable $error = null) {
$this->error = $error;
}
public function isOK () : false {
return false;
}
public function isError () : true {
return true;
}
public function getValue () : mixed {
throw new InvalidArgumentException(<<<'EOD'
This result is an error, so it doesn't have a value.
You can check first with isOK() if this is a value.
Or if you want the error, use getError().
EOD
);
}
public function getError () : Throwable {
return $this->error;
}
public function setError (Throwable $error) : void {
$this->error = $error;
}
public function map (callable $callable) : self {
return $this;
}
public function mapErr (callable $callable) : self {
$error = $callable($this->error);
return new self($error);
}
- public function orElse (mixed $default) : mixed {
+ public function or (Result $default) : Result {
return $default;
}
+
+ public function orElse (callable $callable) : Result {
+ $argc = (new CallableElement($callable))->countArguments();
+
+ return match($argc) {
+ 0 => $callable(),
+ 1 => $callable($this->error),
+
+ default => throw new InvalidArgumentException(
+ self::CB_TOO_MANY_ARGS, 0, $this->error
+ ),
+ };
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $default;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
+ $argc = (new CallableElement($callable))->countArguments();
+
+ return match($argc) {
+ 0 => $callable(),
+ 1 => $callable($this->error),
+
+ default => throw new InvalidArgumentException(
+ self::CB_TOO_MANY_ARGS, 0, $this->error
+ ),
+ };
+ }
}
diff --git a/src/DataTypes/Result/Ok.php b/src/DataTypes/Result/Ok.php
index 86b54f9..ec624a8 100644
--- a/src/DataTypes/Result/Ok.php
+++ b/src/DataTypes/Result/Ok.php
@@ -1,63 +1,75 @@
<?php
namespace Keruald\OmniTools\DataTypes\Result;
use InvalidArgumentException;
use Keruald\OmniTools\Reflection\Type;
class Ok extends Result {
private mixed $value = null;
private string $type = "NULL";
public function __construct ($value = null) {
if ($value !== null) {
$this->setValue($value);
}
}
public function isOK () : true {
return true;
}
public function isError () : false {
return false;
}
public function getValue () : mixed {
return $this->value;
}
public function setValue (mixed $value) : void {
$type = Type::getTypeOf($value);
if (!$this->isAcceptableValueType($type)) {
throw new InvalidArgumentException(<<<'EOD'
When you mutate the value of an Ok object, you can't mutate the object type.
Please consider return a new Ok instead.
EOD
);
}
$this->value = $value;
$this->type = $type;
}
private function isAcceptableValueType (string $type) : bool {
return $this->value === null || $type === $this->type;
}
public function map (callable $callable) : self {
$value = $callable($this->value);
return new self($value);
}
public function mapErr (callable $callable) : self {
return $this;
}
- public function orElse (mixed $default) : mixed {
+ public function or (Result $default) : Result {
+ return $this;
+ }
+
+ public function orElse (callable $callable) : Result {
+ return $this;
+ }
+
+ public function getValueOr (mixed $default) : mixed {
+ return $this->value;
+ }
+
+ public function getValueOrElse (callable $callable) : mixed {
return $this->value;
}
}
diff --git a/src/DataTypes/Result/Result.php b/src/DataTypes/Result/Result.php
index 90d93a5..fca8fad 100644
--- a/src/DataTypes/Result/Result.php
+++ b/src/DataTypes/Result/Result.php
@@ -1,15 +1,51 @@
<?php
namespace Keruald\OmniTools\DataTypes\Result;
abstract class Result {
public abstract function isOK () : bool;
public abstract function isError () : bool;
public abstract function getValue () : mixed;
public abstract function map(callable $callable) : self;
public abstract function mapErr(callable $callable): self;
- public abstract function orElse(mixed $default) : mixed;
+ /**
+ * Returns the result if it is Ok,
+ * or the default result if an Err.
+ *
+ * @param mixed $default The default value
+ * @return mixed
+ */
+ public abstract function or(Result $default) : self;
+
+ /**
+ * Returns the result if it is Ok,
+ * otherwise calls the callable and returns the new result.
+ *
+ * The callable is called only on Err, allowing lazy evaluation.
+ * The callable must return a Result.
+ */
+ public abstract function orElse(callable $callable) : self;
+
+ /**
+ * Returns the value of the result if it is Ok,
+ * or the default value if the option is Err.
+ *
+ * @param mixed $default The default value
+ * @return mixed
+ */
+ public abstract function getValueOr(mixed $default) : mixed;
+
+ /**
+ * Returns the value of the result if it is Ok,
+ * or the result of the callable if the option is Err.
+ *
+ * The callable is called only on Err, allowing lazy evaluation.
+ *
+ * @param callable $callable A function that returns a default value.
+ * @return mixed
+ */
+ public abstract function getValueOrElse(callable $callable) : mixed;
}
diff --git a/src/Events/Propagation.php b/src/Events/Propagation.php
index cb0690c..6513a6e 100644
--- a/src/Events/Propagation.php
+++ b/src/Events/Propagation.php
@@ -1,74 +1,74 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Events;
use BadFunctionCallException;
use Keruald\OmniTools\DataTypes\Option\None;
use Keruald\OmniTools\DataTypes\Option\Option;
use Keruald\OmniTools\DataTypes\Option\Some;
use RuntimeException;
use Throwable;
/**
* Static class providing helper methods to propagate events.
*/
class Propagation {
/**
* Calls a set of functions with the specified parameters.
* This is intended for callback purpose.
*
* @param iterable $callables The functions to call, each item a callable
* @param array $parameters The parameters to pass to the functions [optional]
*/
public static function call (iterable $callables, array $parameters = []) : void {
foreach ($callables as $callable) {
if (!is_callable($callable)) {
$previous = self::grabException($parameters);
- throw new BadFunctionCallException("Callback for this method.", 0, $previous->orElse(null));
+ throw new BadFunctionCallException("Callback for this method.", 0, $previous->getValueOr(null));
}
call_user_func_array($callable, $parameters);
}
}
/**
* Calls a set of functions with the specified parameters.
* If no function is present, throws an exception.
*
* @param iterable $callables The functions to call, each item a callable
* @param array $parameters The parameters to pass to the functions
* [optional]
* @param Throwable|null $exception The exception to throw if no callback is
* provided [optional]
*
* @throws Throwable
*/
public static function callOrThrow (iterable $callables, array $parameters = [], Throwable $exception = null) : void {
if (!count($callables)) {
throw $exception ?? self::grabException($parameters)
- ->orElse(new RuntimeException);
+ ->getValueOrElse(fn () => new RuntimeException);
}
static::call($callables, $parameters);
}
/**
* Grabs the first exception among specified items.
*
* @param iterable $items The items to check
* @return Option<Throwable>
*/
private static function grabException (iterable $items) : Option {
foreach ($items as $item) {
if ($item instanceof Throwable) {
return new Some($item);
}
}
return new None;
}
}
diff --git a/tests/DataTypes/Option/NoneTest.php b/tests/DataTypes/Option/NoneTest.php
index 7809e35..e11b107 100644
--- a/tests/DataTypes/Option/NoneTest.php
+++ b/tests/DataTypes/Option/NoneTest.php
@@ -1,47 +1,68 @@
<?php
namespace Keruald\OmniTools\Tests\DataTypes\Option;
use InvalidArgumentException;
use Keruald\OmniTools\DataTypes\Option\None;
use Keruald\OmniTools\DataTypes\Option\Option;
+use Keruald\OmniTools\DataTypes\Option\Some;
use PHPUnit\Framework\TestCase;
class NoneTest extends TestCase {
private Option $v;
public function setUp () : void {
$this->v = new None;
}
public function testIsSome () : void {
$this->assertFalse($this->v->isSome());
}
public function testIsNone () : void {
$this->assertTrue($this->v->isNone());
}
public function testGetValue () : void {
$this->expectException(InvalidArgumentException::class);
$this->v->getValue();
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals($mapped_v, $this->v);
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(666, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(666, $value);
}
}
diff --git a/tests/DataTypes/Option/SomeTest.php b/tests/DataTypes/Option/SomeTest.php
index d541fe0..daa6227 100644
--- a/tests/DataTypes/Option/SomeTest.php
+++ b/tests/DataTypes/Option/SomeTest.php
@@ -1,57 +1,77 @@
<?php
namespace Keruald\OmniTools\Tests\DataTypes\Option;
use InvalidArgumentException;
use Keruald\OmniTools\DataTypes\Option\Some;
use Keruald\OmniTools\DataTypes\Option\Option;
use PHPUnit\Framework\TestCase;
class SomeTest extends TestCase {
private Option $v;
public function setUp () : void {
$this->v = new Some;
$this->v->setValue(42);
}
public function testIsSome () : void {
$this->assertTrue($this->v->isSome());
}
public function testIsNone () : void {
$this->assertFalse($this->v->isNone());
}
public function testGetValue () : void {
$this->assertEquals(42, $this->v->getValue());
}
public function testSetValue () : void {
$this->v->setValue(666);
$this->assertEquals(666, $this->v->getValue());
}
public function testSetValueWhenTypeIsMutated () : void {
$this->expectException(InvalidArgumentException::class);
$this->v->setValue("Another type");
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals(84, $mapped_v->getValue());
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Some(666));
+
+ $this->assertTrue($actual->isSome());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(42, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(42, $value);
}
}
diff --git a/tests/DataTypes/Result/ErrTest.php b/tests/DataTypes/Result/ErrTest.php
index a9f35c3..f73c186 100644
--- a/tests/DataTypes/Result/ErrTest.php
+++ b/tests/DataTypes/Result/ErrTest.php
@@ -1,56 +1,106 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\DataTypes\Result;
use DivisionByZeroError;
use Exception;
+use InvalidArgumentException;
+use RuntimeException;
use Throwable;
use Keruald\OmniTools\DataTypes\Result\Err;
+use Keruald\OmniTools\DataTypes\Result\Ok;
+
use PHPUnit\Framework\TestCase;
class ErrTest extends TestCase {
public function setUp () : void {
$this->v = new Err;
$this->v->setError(new DivisionByZeroError());
}
public function testIsOk () : void {
$this->AssertFalse($this->v->isOk());
}
public function testIsError () : void {
$this->assertTrue($this->v->isError());
}
public function testGetValue () : void {
$this->expectException("InvalidArgumentException");
$this->v->getValue();
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals($mapped_v, $this->v);
}
public function testMapErr () : void {
$callback = function (Throwable $ex) {
return new Exception();
};
$mapped_v = $this->v->mapErr($callback);
$this->assertInstanceOf(Exception::class, $mapped_v->getError());
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(666, $actual->getValue());
+ }
+
+ public function testOrElseWithCallbackArgument () : void {
+ $actual = $this->v->orElse(
+ fn ($error) => new Err(new RuntimeException(get_class($error)))
+ );
+
+ $this->assertTrue($actual->isError());
+ $this->assertInstanceOf(RuntimeException::class, $actual->getError());
+ $this->assertEquals("DivisionByZeroError", $actual->getError()->getMessage());
+ }
+
+ public function testOrElseWithTooManyCallbackArgument () : void {
+ $this->expectException(InvalidArgumentException::class);
+ $this->v->orElse(fn ($error, $extraneous) => get_class($error));
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(666, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(666, $value);
}
+
+ public function testGetValueOrElseWithCallbackArgument () : void {
+ $actual = $this->v->getValueOrElse(fn ($error) => get_class($error));
+
+ $this->assertEquals("DivisionByZeroError", $actual);
+ }
+
+ public function testGetValueOrElseWithTooManyCallbackArgument () : void {
+ $this->expectException(InvalidArgumentException::class);
+ $this->v->getValueOrElse(fn ($error, $extraneous) => get_class($error));
+ }
}
diff --git a/tests/DataTypes/Result/OkTest.php b/tests/DataTypes/Result/OkTest.php
index c73238d..a880702 100644
--- a/tests/DataTypes/Result/OkTest.php
+++ b/tests/DataTypes/Result/OkTest.php
@@ -1,65 +1,85 @@
<?php
declare(strict_types=1);
namespace Keruald\OmniTools\Tests\DataTypes\Result;
use Exception;
use Keruald\OmniTools\DataTypes\Result\Ok;
use PHPUnit\Framework\TestCase;
class OkTest extends TestCase {
public function setUp () : void {
$this->v = new Ok;
$this->v->setValue(42);
}
public function testIsOk () : void {
$this->AssertTrue($this->v->isOk());
}
public function testIsError () : void {
$this->assertFalse($this->v->isError());
}
public function testGetValue () : void {
$this->assertEquals(42, $this->v->getValue());
}
public function testSetValue () : void {
$this->v->setValue(666);
$this->assertEquals(666, $this->v->getValue());
}
public function testSetValueWhenTypeIsMutated () : void {
$this->expectException("InvalidArgumentException");
$this->v->setValue("Another type");
}
public function testMap () : void {
$callback = function ($n) {
return $n * 2;
};
$mapped_v = $this->v->map($callback);
$this->assertEquals(84, $mapped_v->getValue());
}
public function testMapErr () : void {
$callback = function (Exception $ex) {
return new Exception();
};
$mapped_v = $this->v->mapErr($callback);
$this->assertEquals($mapped_v, $this->v);
}
+ public function testOr () : void {
+ $actual = $this->v->or(new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
public function testOrElse () : void {
- $value = $this->v->orElse(666);
+ $actual = $this->v->orElse(fn () => new Ok(666));
+
+ $this->assertTrue($actual->isOk());
+ $this->assertEquals(42, $actual->getValue());
+ }
+
+ public function testGetValueOr () : void {
+ $value = $this->v->getValueOr(666);
+
+ $this->assertEquals(42, $value);
+ }
+
+ public function testGetValueOrElse () : void {
+ $value = $this->v->getValueOrElse(fn () => 666);
$this->assertEquals(42, $value);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Mar 18, 12:55 (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3539873
Default Alt Text
(24 KB)

Event Timeline