Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Conjunctions/AndConjuntion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Chevere\Workflow\Conjunctions;

use Chevere\Workflow\Interfaces\ConjunctionInterface;

class AndConjuntion extends Conjunction
{
public function conjunction(): string
{
return static::CONJUNCTION_AND;
}
}
102 changes: 102 additions & 0 deletions src/Conjunctions/Conjunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace Chevere\Workflow\Conjunctions;

use Chevere\DataStructure\Interfaces\VectorInterface;
use Chevere\DataStructure\Vector;
use Chevere\Workflow\Interfaces\ConjunctionInterface;
use Chevere\Workflow\Interfaces\ResponseReferenceInterface;
use Chevere\Workflow\Interfaces\VariableInterface;
use Closure;
use Iterator;
use OverflowException;
use function Chevere\Message\message;

abstract class Conjunction implements ConjunctionInterface
{
protected VectorInterface $_conditions;

abstract public function conjunction(): string;

public function __construct(
VariableInterface|ResponseReferenceInterface|ConjunctionInterface|callable ...$conditions
)
{
$this->putConditions(...$conditions);
}

public function conditions(): VectorInterface
{
return $this->_conditions;
}

public function toArray(): array
{
return array_values(
$this->conditions()->toArray()
);
}

public function getIterator(): Iterator
{
return $this
->conditions()
->getIterator();
}

public function withPut(
VariableInterface|ResponseReferenceInterface|ConjunctionInterface|callable ...$conditions
): static
{
return (clone $this)
->putConditions(...$conditions);
}

/**
* @return array<int,string>
*/
public function keys(): array
{
return array_values(
$this->conditions()->keys()
);
}

protected function putConditions(
VariableInterface|ResponseReferenceInterface|ConjunctionInterface|callable ...$conditions
): static
{
$known = new Vector();
$this->_conditions = new Vector();

foreach ($conditions as $condition) {
$id = match (true) {
$condition instanceof ResponseReferenceInterface,
$condition instanceof VariableInterface => $condition->__toString(),
$condition instanceof ConjunctionInterface => 'conjunction#' . spl_object_id($condition),
$condition instanceof Closure => 'callable#' . spl_object_id($condition),
default => null,
};

if ( $id !== null && $known->contains($id) ) {
throw new OverflowException(
(string) message(
'Condition `%condition%` is already defined',
condition: $id
)
);
}

if ($id !== null) {
$known = $known
->withPush($id);

$this->_conditions = $this
->conditions()
->withPush($condition);
}
}

return $this;
}
}
13 changes: 13 additions & 0 deletions src/Conjunctions/NorConjunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Chevere\Workflow\Conjunctions;

use Chevere\Workflow\Interfaces\ConjunctionInterface;

class NorConjunction extends Conjunction
{
public function conjunction(): string
{
return static::CONJUNCTION_NOR;
}
}
13 changes: 13 additions & 0 deletions src/Conjunctions/OrConjunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Chevere\Workflow\Conjunctions;

use Chevere\Workflow\Interfaces\ConjunctionInterface;

class OrConjunction extends Conjunction
{
public function conjunction(): string
{
return static::CONJUNCTION_OR;
}
}
25 changes: 25 additions & 0 deletions src/Interfaces/ConjunctionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Chevere\Workflow\Interfaces;

use Chevere\DataStructure\Interfaces\VectorInterface;
use Iterator;

interface ConjunctionInterface
{
const string CONJUNCTION_AND = 'and';
const string CONJUNCTION_OR = 'or';
const string CONJUNCTION_NOR = 'nor';

public function conjunction(): string;

public function conditions(): VectorInterface;

public function keys(): array;

public function toArray(): array;

public function getIterator(): Iterator;

public function withPut(VariableInterface|ResponseReferenceInterface|ConjunctionInterface|callable ...$conditions): static;
}
6 changes: 5 additions & 1 deletion src/Job.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Chevere\DataStructure\Vector;
use Chevere\Parameter\Interfaces\ParameterInterface;
use Chevere\Parameter\Interfaces\ParametersInterface;
use Chevere\Workflow\Interfaces\ConjunctionInterface;
use Chevere\Workflow\Interfaces\JobInterface;
use Chevere\Workflow\Interfaces\ResponseReferenceInterface;
use Chevere\Workflow\Interfaces\VariableInterface;
Expand Down Expand Up @@ -97,7 +98,9 @@ public function withArguments(mixed ...$argument): JobInterface
return $new;
}

public function withRunIf(ResponseReferenceInterface|VariableInterface|callable ...$context): JobInterface
public function withRunIf(
ConjunctionInterface|ResponseReferenceInterface|VariableInterface|callable ...$context
): JobInterface
{
$new = clone $this;
$new->runIf = new Vector();
Expand All @@ -106,6 +109,7 @@ public function withRunIf(ResponseReferenceInterface|VariableInterface|callable
$itemString = match (true) {
$item instanceof ResponseReferenceInterface,
$item instanceof VariableInterface => $item->__toString(),
$item instanceof ConjunctionInterface => 'conjunction#' . spl_object_id($item),
$item instanceof Closure => 'callable#' . spl_object_id($item),
default => null,
};
Expand Down
92 changes: 75 additions & 17 deletions src/Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,22 @@
namespace Chevere\Workflow;

use Amp\Parallel\Worker\Execution;
use Chevere\Parameter\Interfaces\BoolParameterInterface;
use Chevere\Parameter\Interfaces\CastInterface;
use Chevere\Workflow\Conjunctions\NorConjunction;
use Chevere\Workflow\Exceptions\RunnerException;
use Chevere\Workflow\Interfaces\ConjunctionInterface;
use Chevere\Workflow\Interfaces\JobInterface;
use Chevere\Workflow\Interfaces\ResponseReferenceInterface;
use Chevere\Workflow\Interfaces\RunInterface;
use Chevere\Workflow\Interfaces\RunnerInterface;
use Chevere\Workflow\Interfaces\VariableInterface;
use InvalidArgumentException;
use OutOfBoundsException;
use Throwable;
use function Amp\Future\await;
use function Amp\Parallel\Worker\submit;
use function Chevere\Message\message;
use function Chevere\Parameter\cast;

final class Runner implements RunnerInterface
Expand Down Expand Up @@ -104,15 +109,37 @@ public function withRunJob(string $name): RunnerInterface
return $new;
}

private function getRunIfCondition(VariableInterface|ResponseReferenceInterface|callable $runIf): bool
private function getRunIfCondition(
ConjunctionInterface|VariableInterface|ResponseReferenceInterface|callable $runIf
): bool
{
if ($runIf instanceof ConjunctionInterface) {
$results = [];

foreach ($runIf->getIterator() as $runIfCondition) {
$results[] = $this
->getRunIfCondition($runIfCondition);
}

$filter = array_filter($results);

return match ($runIf->conjunction()) {
ConjunctionInterface::CONJUNCTION_NOR => empty($filter),
ConjunctionInterface::CONJUNCTION_OR => !empty($filter),
ConjunctionInterface::CONJUNCTION_AND => ($results == $filter),
default => false
};
}

/** @var boolean */
return match(true) {
$runIf instanceof VariableInterface =>
$this->run->arguments()->required($runIf->__toString())->bool(),

$runIf instanceof ResponseReferenceInterface =>
$this->run->getReturn($runIf->job())->array()[$runIf->key()],
($this->run->workflow()->jobs()->get($runIf->job())->action()::return() instanceof BoolParameterInterface) ?
$this->run->response($runIf->job())->bool() :
$this->run->response($runIf->job())->array()[$runIf->key()],

default =>
call_user_func($runIf, $this->run())
Expand All @@ -126,30 +153,61 @@ private function getJobArguments(JobInterface $job): array
{
$arguments = [];
foreach ($job->arguments() as $name => $value) {
$isResponseReference = $value instanceof ResponseReferenceInterface;
$isVariable = $value instanceof VariableInterface;
if (! ($isResponseReference || $isVariable)) {
$arguments[$name] = $value;
if ($value instanceof VariableInterface ||
$value instanceof ResponseReferenceInterface ||
$value instanceof ConjunctionInterface
) {
try {
$arguments[$name] = $this
->processJobArgument($value);

} catch (Throwable $e) {
throw new RunnerException($name, $job, $e);
}

continue;
}
if ($isVariable) {
/** @var VariableInterface $value */
$arguments[$name] = $this->run->arguments()
->get($value->__toString());

continue;
$arguments[$name] = $value;
}

return $arguments;
}

private function processJobArgument(
ConjunctionInterface|VariableInterface|ResponseReferenceInterface|callable $value
): mixed
{
if ($value instanceof ConjunctionInterface) {
if ($value->conjunction() !== ConjunctionInterface::CONJUNCTION_OR) {
throw new RunnerException('');
}
/** @var ResponseReferenceInterface $value */
if ($value->key() !== null) {
$arguments[$name] = $this->run->response($value->job())->array()[$value->key()];

continue;
/** @var ConjunctionInterface|VariableInterface|ResponseReferenceInterface|callable $conditional */
foreach ($value->getIterator() as $conditional) {
$val = $this
->processJobArgument($conditional);

if ($val !== null) {
return $val;
}
}
$arguments[$name] = $this->run->response($value->job())->mixed();

throw new InvalidArgumentException();
}

return $arguments;
return match(true) {
$value instanceof VariableInterface =>
$this->run->arguments()->get($value->__toString()),

$value instanceof ResponseReferenceInterface =>
($value->key() !== null) ?
$this->run->response($value->job())->array()[$value->key()] :
$this->run->response($value->job())->mixed(),

default =>
call_user_func($value, $this->run())
};
}

private function addJobResponse(string $name, CastInterface $response): void
Expand Down
35 changes: 35 additions & 0 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
namespace Chevere\Workflow;

use Chevere\Action\Interfaces\ActionInterface;
use Chevere\Workflow\Conjunctions\AndConjuntion;
use Chevere\Workflow\Conjunctions\Conjunction;
use Chevere\Workflow\Conjunctions\NorConjunction;
use Chevere\Workflow\Conjunctions\OrConjunction;
use Chevere\Workflow\Interfaces\ConjunctionInterface;
use Chevere\Workflow\Interfaces\JobInterface;
use Chevere\Workflow\Interfaces\ResponseReferenceInterface;
use Chevere\Workflow\Interfaces\RunInterface;
Expand Down Expand Up @@ -101,6 +106,36 @@ function runnerForJob(RunnerInterface $runner, string $job): RunnerInterface
return $runner->withRunJob($job);
}

/**
* Creates an AND style Conjunction
* @param ConjunctionInterface|ResponseReferenceInterface|VariableInterface|callable ...$conditions
* @return ConjunctionInterface
*/
function cand(ConjunctionInterface|ResponseReferenceInterface|VariableInterface|callable ...$conditions): ConjunctionInterface
{
return new AndConjuntion(...$conditions);
}

/**
* Creates an OR style Conjunction
* @param ConjunctionInterface|ResponseReferenceInterface|VariableInterface|callable ...$conditions
* @return ConjunctionInterface
*/
function cor(ConjunctionInterface|ResponseReferenceInterface|VariableInterface|callable ...$conditions): ConjunctionInterface
{
return new OrConjunction(...$conditions);
}

/**
* Creates a NOR style Conjunction
* @param ConjunctionInterface|ResponseReferenceInterface|VariableInterface|callable ...$conditions
* @return ConjunctionInterface
*/
function cnor(ConjunctionInterface|ResponseReferenceInterface|VariableInterface|callable ...$conditions): ConjunctionInterface
{
return new NorConjunction(...$conditions);
}

/**
* Creates a RunInterface instance for the given workflow and named variables .
*/
Expand Down