-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Baseline implementation of MermaidJS Output Formatter - qossmic/deptr…
…ac#1372 (#20) Feat: base implementation of MermaidJS Output Formatter --------- Co-authored-by: simbera <jan.simbera@onlineprinters.com>
- Loading branch information
1 parent
7788ac6
commit 842a8c6
Showing
10 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Qossmic\Deptrac\Contract\Config\Formatter; | ||
|
||
use Qossmic\Deptrac\Contract\Config\Layer; | ||
|
||
final class MermaidJsConfig implements FormatterConfigInterface | ||
{ | ||
private string $name = 'mermaidjs'; | ||
|
||
private string $direction = 'TD'; | ||
|
||
/** @var array<string, Layer[]> */ | ||
private array $groups = []; | ||
|
||
public static function create(): self | ||
{ | ||
return new self(); | ||
} | ||
|
||
public function getName(): string | ||
{ | ||
return $this->name; | ||
} | ||
|
||
public function direction(string $direction): self | ||
{ | ||
$this->direction = $direction; | ||
|
||
return $this; | ||
} | ||
|
||
public function groups(string $name, Layer ...$layerConfigs): self | ||
{ | ||
foreach ($layerConfigs as $layerConfig) { | ||
$this->groups[$name][] = $layerConfig; | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
public function toArray(): array | ||
{ | ||
$output = []; | ||
|
||
if ([] !== $this->groups) { | ||
$output['groups'] = array_map( | ||
static fn (array $configs) => array_map(static fn (Layer $layer) => $layer->name, $configs), | ||
$this->groups | ||
); | ||
} | ||
|
||
$output['direction'] = $this->direction; | ||
|
||
return $output; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
src/Supportive/OutputFormatter/MermaidJSOutputFormatter.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Qossmic\Deptrac\Supportive\OutputFormatter; | ||
|
||
use Qossmic\Deptrac\Contract\OutputFormatter\OutputFormatterInput; | ||
use Qossmic\Deptrac\Contract\OutputFormatter\OutputFormatterInterface; | ||
use Qossmic\Deptrac\Contract\OutputFormatter\OutputInterface; | ||
use Qossmic\Deptrac\Contract\Result\OutputResult; | ||
use Qossmic\Deptrac\Supportive\OutputFormatter\Configuration\FormatterConfiguration; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
final class MermaidJSOutputFormatter implements OutputFormatterInterface | ||
{ | ||
/** @var array{direction: string, groups: array<string, string[]>} */ | ||
private array $config; | ||
private const GRAPH_TYPE = 'flowchart %s;'; | ||
|
||
private const GRAPH_END = ' end;'; | ||
private const SUBGRAPH = ' subgraph %sGroup;'; | ||
private const LAYER = ' %s;'; | ||
private const GRAPH_NODE_FORMAT = ' %s -->|%d| %s;'; | ||
private const VIOLATION_STYLE_FORMAT = ' linkStyle %d stroke:red,stroke-width:4px;'; | ||
|
||
public function __construct(FormatterConfiguration $config) | ||
{ | ||
/** @var array{direction: string, groups: array<string, string[]>} $extractedConfig */ | ||
$extractedConfig = $config->getConfigFor('mermaidjs'); | ||
$this->config = $extractedConfig; | ||
} | ||
|
||
public static function getName(): string | ||
{ | ||
return 'mermaidjs'; | ||
} | ||
|
||
public function finish( | ||
OutputResult $result, | ||
OutputInterface $output, | ||
OutputFormatterInput $outputFormatterInput | ||
): void { | ||
$graph = $this->parseResults($result); | ||
$violations = $result->violations(); | ||
$buffer = ''; | ||
|
||
$buffer .= sprintf(self::GRAPH_TYPE.PHP_EOL, $this->config['direction']); | ||
|
||
foreach ($this->config['groups'] as $subGraphName => $layers) { | ||
$buffer .= sprintf(self::SUBGRAPH.PHP_EOL, $subGraphName); | ||
|
||
foreach ($layers as $layer) { | ||
$buffer .= sprintf(self::LAYER.PHP_EOL, $layer); | ||
} | ||
|
||
$buffer .= self::GRAPH_END.PHP_EOL; | ||
} | ||
|
||
$linkCount = 0; | ||
$violationsLinks = []; | ||
$violationGraphLinks = []; | ||
|
||
foreach ($violations as $violation) { | ||
if (!isset($violationsLinks[$violation->getDependerLayer()][$violation->getDependentLayer()])) { | ||
$violationsLinks[$violation->getDependerLayer()][$violation->getDependentLayer()] = 1; | ||
} else { | ||
++$violationsLinks[$violation->getDependerLayer()][$violation->getDependentLayer()]; | ||
} | ||
} | ||
|
||
foreach ($violationsLinks as $dependerLayer => $layers) { | ||
foreach ($layers as $dependentLayer => $count) { | ||
$buffer .= sprintf(self::GRAPH_NODE_FORMAT.PHP_EOL, $dependerLayer, $count, $dependentLayer); | ||
$violationGraphLinks[] = $linkCount; | ||
++$linkCount; | ||
} | ||
} | ||
|
||
foreach ($graph as $dependerLayer => $layers) { | ||
foreach ($layers as $dependentLayer => $count) { | ||
if (!isset($violationsLinks[$dependerLayer][$dependentLayer])) { | ||
$buffer .= sprintf(self::GRAPH_NODE_FORMAT.PHP_EOL, $dependerLayer, $count, $dependentLayer); | ||
} | ||
} | ||
} | ||
|
||
foreach ($violationGraphLinks as $linkNumber) { | ||
$buffer .= sprintf(self::VIOLATION_STYLE_FORMAT.PHP_EOL, $linkNumber); | ||
} | ||
|
||
if (null !== $outputFormatterInput->outputPath) { | ||
file_put_contents($outputFormatterInput->outputPath, $buffer); | ||
} else { | ||
$output->writeRaw($buffer); | ||
} | ||
} | ||
|
||
/** | ||
* @return array<string, array<string, int<1, max>>> | ||
*/ | ||
protected function parseResults(OutputResult $result): array | ||
{ | ||
$graph = []; | ||
|
||
foreach ($result->allowed() as $rule) { | ||
if (!isset($graph[$rule->getDependerLayer()][$rule->getDependentLayer()])) { | ||
$graph[$rule->getDependerLayer()][$rule->getDependentLayer()] = 1; | ||
} else { | ||
++$graph[$rule->getDependerLayer()][$rule->getDependentLayer()]; | ||
} | ||
} | ||
|
||
return $graph; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.