Skip to content

Commit

Permalink
Tweaking the public API and internal names for consistence
Browse files Browse the repository at this point in the history
  • Loading branch information
dakujem committed Jan 29, 2024
1 parent ad248dc commit c2d4c96
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 39 deletions.
11 changes: 9 additions & 2 deletions src/MaterializedPath/Path.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Dakujem\Oliva\MaterializedPath;

use Dakujem\Oliva\TreeNodeContract;
use LogicException;

/**
Expand All @@ -25,7 +26,10 @@ public static function delimited(string $delimiter, callable $accessor): callabl
if (strlen($delimiter) !== 1) {
throw new LogicException('The delimiter must be a single character.');
}
return function (mixed $data) use ($delimiter, $accessor): array {
return function (mixed $data, mixed $inputIndex = null, ?TreeNodeContract $node = null) use (
$delimiter,
$accessor,
): array {
$path = $accessor($data);
if (null === $path) {
return [];
Expand Down Expand Up @@ -53,7 +57,10 @@ public static function delimited(string $delimiter, callable $accessor): callabl
*/
public static function fixed(int $levelWidth, callable $accessor): callable
{
return function (mixed $data) use ($levelWidth, $accessor): array {
return function (mixed $data, mixed $inputIndex = null, ?TreeNodeContract $node = null) use (
$levelWidth,
$accessor,
): array {
$path = $accessor($data);
if (null === $path || $path === '') {
return [];
Expand Down
6 changes: 3 additions & 3 deletions src/MaterializedPath/TreeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ final class TreeBuilder
* signature `fn(mixed $data): MovableNodeContract`.
* @var callable
*/
private $node;
private $factory;

/**
* Extractor of the node vector,
Expand All @@ -63,7 +63,7 @@ public function __construct(
callable $node,
callable $vector,
) {
$this->node = $node;
$this->factory = $node;
$this->vector = $vector;
}

Expand All @@ -80,7 +80,7 @@ public function processInput(iterable $input): AlmostThere
{
$shadowRoot = $this->buildShadowTree(
$input,
$this->node,
$this->factory,
$this->vector,
);

Expand Down
4 changes: 2 additions & 2 deletions src/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function children(): array
}

/**
* Discover whether the given node is one or the given key points to one of this node's children.
* Discover whether the given node is one of this node's children (or the given key points to one of them).
*/
public function hasChild(TreeNodeContract|string|int $child): bool
{
Expand All @@ -58,7 +58,7 @@ public function hasChild(TreeNodeContract|string|int $child): bool

/**
* Get a specific child, if possible.
* Returns `null` when there is no such a child.
* Returns `null` when there is no such child.
*/
public function child(int|string $key): ?TreeNodeContract
{
Expand Down
59 changes: 37 additions & 22 deletions src/Recursive/TreeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@ final class TreeBuilder
* signature `fn(mixed $data): MovableNodeContract`.
* @var callable
*/
private $node;
private $factory;

/**
* Extractor of the self reference,
* signature `fn(mixed $data, mixed $inputIndex, TreeNodeContract $node): string|int`.
* @var callable
*/
private $self;
private $selfRef;

/**
* Extractor of the parent reference,
* signature `fn(mixed $data, mixed $inputIndex, TreeNodeContract $node): string|int|null`.
* @var callable
*/
private $parent;
private $parentRef;

/**
* Callable that detects the root node.
Expand All @@ -58,18 +58,24 @@ final class TreeBuilder

public function __construct(
callable $node,
callable $self,
callable $parent,
callable $selfRef,
callable $parentRef,
string|int|callable|null $root = null,
) {
$this->node = $node;
$this->self = $self;
$this->parent = $parent;
$this->factory = $node;
$this->selfRef = $selfRef;
$this->parentRef = $parentRef;
if (null === $root || is_string($root) || is_int($root)) {
// By default, the root node is detected by having the parent ref equal to `null`.
// By passing in a string or an integer, the root node will be detected by comparing that value to the node's parent value.
// For custom "is root" detector, use a callable.
$this->root = fn($data, $inputIndex, $node, $parentRef, $selfRef): bool => $parentRef === $root;
$this->root = fn(
mixed $data,
mixed $inputIndex,
TreeNodeContract $node,
int|string|null $parentReference,
int|string|null $selfReference,
): bool => $parentReference === $root;
} elseif (is_callable($root)) {
$this->root = $root;
} else {
Expand All @@ -81,9 +87,9 @@ public function build(iterable $input): TreeNodeContract
{
[$root] = $this->processData(
input: $input,
nodeFactory: $this->node,
selfRefExtractor: $this->self,
parentRefExtractor: $this->parent,
nodeFactory: $this->factory,
selfRefExtractor: $this->selfRef,
parentRefExtractor: $this->parentRef,
isRoot: $this->root,
);
return $root;
Expand Down Expand Up @@ -121,26 +127,35 @@ private function processData(
throw new LogicException('The node factory must return a movable node instance.');
}

$self = $selfRefExtractor($data, $inputIndex, $node);
$parent = $parentRefExtractor($data, $inputIndex, $node);
$selfRef = $selfRefExtractor($data, $inputIndex, $node);
$parentRef = $parentRefExtractor($data, $inputIndex, $node);

if (isset($nodeRegister[$self])) {
if (!is_string($selfRef) && !is_int($selfRef)) {
// TODO improve exceptions
throw new LogicException('Duplicate node reference: ' . $self);
throw new LogicException('Invalid "self reference" returned by the extractor. Requires a int|string unique to the given node.');
}
$nodeRegister[$self] = $node;
if (null !== $parentRef && !is_string($parentRef) && !is_int($parentRef)) {
// TODO improve exceptions
throw new LogicException('Invalid "parent reference" returned by the extractor. Requires a int|string uniquely pointing to "self reference" of another node, or `null`.');
}

if (isset($nodeRegister[$selfRef])) {
// TODO improve exceptions
throw new LogicException('Duplicate node reference: ' . $selfRef);
}
$nodeRegister[$selfRef] = $node;

// When this node is the root, it has no parent.
if (!$rootFound && $isRoot($data, $inputIndex, $node, $parent, $self)) {
$rootRef = $self;
if (!$rootFound && $isRoot($data, $inputIndex, $node, $parentRef, $selfRef)) {
$rootRef = $selfRef;
$rootFound = true;
continue;
}

if (!isset($childRegister[$parent])) {
$childRegister[$parent] = [];
if (!isset($childRegister[$parentRef])) {
$childRegister[$parentRef] = [];
}
$childRegister[$parent][] = $self;
$childRegister[$parentRef][] = $selfRef;
}

if (!$rootFound) {
Expand Down
6 changes: 4 additions & 2 deletions src/Simple/NodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ public function __construct(
$this->factory = $node;
}

public function node(mixed $data, iterable $children = []): TreeNodeContract
{
public function node(
mixed $data,
iterable $children = [],
): TreeNodeContract {
// Build the node.
$node = ($this->factory)($data);

Expand Down
5 changes: 3 additions & 2 deletions src/Simple/TreeWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ public function __construct(
$this->childrenExtractor = $children;
}

public function wrap(mixed $data): TreeNodeContract
{
public function wrap(
mixed $data,
): TreeNodeContract {
return $this->wrapNode(
data: $data,
nodeFactory: $this->factory,
Expand Down
6 changes: 4 additions & 2 deletions src/TreeNodeContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,20 @@ public function parent(): ?TreeNodeContract;
/**
* Get the node's children.
*
* The implementations MUST ensure the keys are valid PHP array keys (only int or string).
*
* @return iterable<int|string,TreeNodeContract>
*/
public function children(): iterable;

/**
* Discover whether the given node is one or the given key points to one of this node's children.
* Discover whether the given node is one of this node's children (or the given key points to one of them).
*/
public function hasChild(TreeNodeContract|string|int $child): bool;

/**
* Get a specific child, if possible.
* Returns `null` when there is no such a child.
* Returns `null` when there is no such child.
*/
public function child(string|int $key): ?TreeNodeContract;

Expand Down
8 changes: 4 additions & 4 deletions tests/recursive.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ class Item

$builder = new TreeBuilder(
node: fn(?Item $item) => new Node($item),
self: fn(?Item $item) => $item?->id,
parent: fn(?Item $item) => $item?->parent,
selfRef: fn(?Item $item) => $item?->id,
parentRef: fn(?Item $item) => $item?->parent,
);

$tree = $builder->build(
Expand All @@ -70,8 +70,8 @@ class Item

$builder = new TreeBuilder(
node: fn(?Item $item) => new Node($item),
self: fn(?Item $item) => $item?->id,
parent: fn(?Item $item) => $item?->parent,
selfRef: fn(?Item $item) => $item?->id,
parentRef: fn(?Item $item) => $item?->parent,
);

$tree = $builder->build(
Expand Down

0 comments on commit c2d4c96

Please sign in to comment.