Skip to content

Commit

Permalink
Error handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
dakujem committed Jan 29, 2024
1 parent 92614da commit 11679b5
Show file tree
Hide file tree
Showing 24 changed files with 407 additions and 79 deletions.
37 changes: 37 additions & 0 deletions src/Exceptions/AcceptDebugContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

/**
* @see AcceptsDebugContext
* @internal
*
* @author Andrej Rypak <[email protected]>
*/
trait AcceptDebugContext
{
public ?Context $context = null;

// public function context(callable $code): self
// {
// $this->context ??= new Context();
// $code($this->context, $this);
// return $this;
// }

public function tag(string $key, mixed $value): self
{
$this->context ??= new Context();
$this->context->tag($key, $value);
return $this;
}

public function push(string $key, mixed $value): self
{
$this->context ??= new Context();
$this->context->push($key, $value);
return $this;
}
}
19 changes: 19 additions & 0 deletions src/Exceptions/AcceptsDebugContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

/**
* @internal
*
* @author Andrej Rypak <[email protected]>
*/
interface AcceptsDebugContext
{
// public function context(callable $code): self;

public function tag(string $key, mixed $value): self;

public function push(string $key, mixed $value): self;
}
17 changes: 17 additions & 0 deletions src/Exceptions/ChildKeyCollision.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

use RuntimeException;

/**
* Thrown when a child node is being added with a key that would overwrite another child with the same key.
*
* @author Andrej Rypak <[email protected]>
*/
final class ChildKeyCollision extends RuntimeException implements FailsIntegrity, AcceptsDebugContext
{
use AcceptDebugContext;
}
16 changes: 16 additions & 0 deletions src/Exceptions/ConfigurationIssue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

use LogicException;

/**
* ConfigurationIssue
*
* @author Andrej Rypak <[email protected]>
*/
final class ConfigurationIssue extends LogicException implements FailsIntegrity
{
}
30 changes: 30 additions & 0 deletions src/Exceptions/Context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

/**
* Context meant for debugging issues.
*
* @author Andrej Rypak <[email protected]>
*/
final class Context
{
public array $bag = [];

public function tag(string $key, mixed $value): self
{
$this->bag[$key] = $value;
return $this;
}

public function push(string $key, mixed $value): self
{
if (!isset($this->bag[$key])) {
$this->bag[$key] = [];
}
$this->bag[$key][] = $value;
return $this;
}
}
17 changes: 17 additions & 0 deletions src/Exceptions/ExtractorReturnValueIssue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

use LogicException;

/**
* A problem with return value of an extractor.
*
* @author Andrej Rypak <[email protected]>
*/
final class ExtractorReturnValueIssue extends LogicException implements FailsIntegrity, AcceptsDebugContext
{
use AcceptDebugContext;
}
15 changes: 15 additions & 0 deletions src/Exceptions/FailsIntegrity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

/**
* Base interface for exceptions indicating a problem on the integration side,
* be it either a configuration issue or invalid source data.
*
* @author Andrej Rypak <[email protected]>
*/
interface FailsIntegrity extends IndicatesTreeIssue
{
}
18 changes: 18 additions & 0 deletions src/Exceptions/IndicatesInternalFault.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

/**
* Indicates internal logic fault.
* Implementors should never see these being thrown.
*
* @internal
*
* @author Andrej Rypak <[email protected]>
*/
interface IndicatesInternalFault extends IndicatesTreeIssue
{

}
16 changes: 16 additions & 0 deletions src/Exceptions/IndicatesTreeIssue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

/**
* Base exception interface for Oliva-specific exceptions.
* This can be used to catch all exceptions thrown by Oliva.
*
* @author Andrej Rypak <[email protected]>
*/
interface IndicatesTreeIssue
{

}
16 changes: 16 additions & 0 deletions src/Exceptions/InternalLogicException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

use LogicException;

/**
* Sorry.
*
* @author Andrej Rypak <[email protected]>
*/
final class InternalLogicException extends LogicException implements IndicatesInternalFault
{
}
17 changes: 17 additions & 0 deletions src/Exceptions/InvalidInputData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

use RuntimeException;

/**
* Indicates invalid input data or argument.
*
* @author Andrej Rypak <[email protected]>
*/
final class InvalidInputData extends RuntimeException implements FailsIntegrity, AcceptsDebugContext
{
use AcceptDebugContext;
}
29 changes: 29 additions & 0 deletions src/Exceptions/InvalidNodeFactoryReturnValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

use Dakujem\Oliva\MovableNodeContract;
use LogicException;
use Throwable;

/**
* Every node factory callable must return a movable node implementation instance, otherwise the builders can not work.
* @see MovableNodeContract
*
* @author Andrej Rypak <[email protected]>
*/
final class InvalidNodeFactoryReturnValue extends LogicException implements FailsIntegrity, AcceptsDebugContext
{
use AcceptDebugContext;

public function __construct($message = null, $code = null, Throwable $previous = null)
{
parent::__construct(
$message ?? ('The node factory must return a movable node instance (' . MovableNodeContract::class . ').'),
$code ?? 0,
$previous,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@

declare(strict_types=1);

namespace Dakujem\Oliva\MaterializedPath;
namespace Dakujem\Oliva\Exceptions;

use RuntimeException;
use Throwable;

/**
* @author Andrej Rypak <[email protected]>
*/
final class InvalidTreePath extends RuntimeException
final class InvalidTreePath extends RuntimeException implements FailsIntegrity, AcceptsDebugContext
{
use AcceptDebugContext;

public function __construct($message = null, $code = null, Throwable $previous = null)
{
parent::__construct($message ?? 'The given value is not a valid tree path.', $code ?? 0, $previous);
Expand Down
26 changes: 26 additions & 0 deletions src/Exceptions/NodeNotMovable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Dakujem\Oliva\Exceptions;

use Dakujem\Oliva\MovableNodeContract;
use LogicException;
use Throwable;

/**
* Builders can only work with movable nodes:
* @see MovableNodeContract
*
* @author Andrej Rypak <[email protected]>
*/
final class NodeNotMovable extends LogicException implements IndicatesTreeIssue
{
public mixed $node;

public function __construct(mixed $node, $message = null, $code = null, Throwable $previous = null)
{
$this->node = $node;
parent::__construct($message ?? 'Encountered a non-movable node while manipulating a tree.', $code ?? 0, $previous);
}
}
19 changes: 13 additions & 6 deletions src/MaterializedPath/Path.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace Dakujem\Oliva\MaterializedPath;

use Dakujem\Oliva\Exceptions\ConfigurationIssue;
use Dakujem\Oliva\Exceptions\InvalidTreePath;
use Dakujem\Oliva\TreeNodeContract;
use LogicException;

/**
* @author Andrej Rypak <[email protected]>
Expand All @@ -24,7 +25,7 @@ final class Path
public static function delimited(string $delimiter, callable $accessor): callable
{
if (strlen($delimiter) !== 1) {
throw new LogicException('The delimiter must be a single character.');
throw new ConfigurationIssue('The delimiter must be a single character.');
}
return function (mixed $data, mixed $inputIndex = null, ?TreeNodeContract $node = null) use (
$delimiter,
Expand All @@ -35,8 +36,11 @@ public static function delimited(string $delimiter, callable $accessor): callabl
return [];
}
if (!is_string($path)) {
// TODO improve exceptions (index/path etc)
throw new InvalidTreePath('Invalid tree path returned by the accessor. A string is required.');
throw (new InvalidTreePath('Invalid tree path returned by the accessor. A string is required.'))
->tag('path', $path)
->tag('data', $data)
->tag('index', $inputIndex)
->tag('node', $node);
}
$path = trim($path, $delimiter);
if ('' === $path) {
Expand Down Expand Up @@ -66,8 +70,11 @@ public static function fixed(int $levelWidth, callable $accessor): callable
return [];
}
if (!is_string($path)) {
// TODO improve exceptions (index/path etc)
throw new InvalidTreePath('Invalid tree path returned by the accessor. A string is required.');
throw (new InvalidTreePath('Invalid tree path returned by the accessor. A string is required.'))
->tag('path', $path)
->tag('data', $data)
->tag('index', $inputIndex)
->tag('node', $node);
}
return str_split($path, $levelWidth);
};
Expand Down
5 changes: 3 additions & 2 deletions src/MaterializedPath/Support/ShadowNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Dakujem\Oliva\MaterializedPath\Support;

use Dakujem\Oliva\Exceptions\InternalLogicException;
use Dakujem\Oliva\MovableNodeContract;
use Dakujem\Oliva\Node;
use Dakujem\Oliva\TreeNodeContract;
Expand Down Expand Up @@ -58,15 +59,15 @@ public function realNode(): ?MovableNodeContract
public function addChild(TreeNodeContract $child, string|int|null $key = null): self
{
if (!$child instanceof self) {
throw new LogicException('Invalid use of a shadow node. Only shadow nodes can be children of shadow nodes.');
throw new InternalLogicException('Invalid use of a shadow node. Only shadow nodes can be children of shadow nodes.');
}
return parent::addChild($child, $key);
}

public function setParent(?TreeNodeContract $parent): self
{
if (!$parent instanceof self) {
throw new LogicException('Invalid use of a shadow node. Only shadow nodes can be parents of shadow nodes.');
throw new InternalLogicException('Invalid use of a shadow node. Only shadow nodes can be parents of shadow nodes.');
}
return parent::setParent($parent);
}
Expand Down
Loading

0 comments on commit 11679b5

Please sign in to comment.