Skip to content

Commit 869b071

Browse files
committed
Refactor ImportResolver path prefixing into dedicated services
- Extract path prefix functionality into a dedicated component - Add comprehensive tests for all new components
1 parent 8b9f642 commit 869b071

10 files changed

+655
-114
lines changed

src/ConfigLoader/Import/ImportConfig.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ public function __construct(
2424
public bool $hasWildcard = false,
2525
) {}
2626

27-
public function configDirectory(bool $absolute = false): string
28-
{
29-
return \dirname($absolute ? $this->absolutePath : $this->path);
30-
}
31-
3227
/**
3328
* Create from an array configuration
3429
*
@@ -78,6 +73,11 @@ public static function fromArray(array $config, string $basePath): self
7873
);
7974
}
8075

76+
public function configDirectory(bool $absolute = false): string
77+
{
78+
return \dirname($absolute ? $this->absolutePath : $this->path);
79+
}
80+
8181
/**
8282
* Resolve a relative path to an absolute path
8383
*/

src/ConfigLoader/Import/ImportResolver.php

+11-107
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use Butschster\ContextGenerator\ConfigLoader\ConfigLoaderFactoryInterface;
88
use Butschster\ContextGenerator\ConfigLoader\Exception\ConfigLoaderException;
9+
use Butschster\ContextGenerator\ConfigLoader\Import\PathPrefixer\DocumentOutputPathPrefixer;
10+
use Butschster\ContextGenerator\ConfigLoader\Import\PathPrefixer\SourcePathPrefixer;
911
use Butschster\ContextGenerator\Directories;
1012
use Butschster\ContextGenerator\FilesInterface;
1113
use Psr\Log\LoggerInterface;
@@ -16,6 +18,8 @@
1618
final readonly class ImportResolver
1719
{
1820
private WildcardPathFinder $pathFinder;
21+
private DocumentOutputPathPrefixer $documentPrefixer;
22+
private SourcePathPrefixer $sourcePrefixer;
1923

2024
public function __construct(
2125
private Directories $dirs,
@@ -24,6 +28,8 @@ public function __construct(
2428
private ?LoggerInterface $logger = null,
2529
) {
2630
$this->pathFinder = new WildcardPathFinder($files, $logger);
31+
$this->documentPrefixer = new DocumentOutputPathPrefixer();
32+
$this->sourcePrefixer = new SourcePathPrefixer();
2733
}
2834

2935
/**
@@ -98,7 +104,7 @@ private function processWildcardImport(
98104
ImportConfig $importCfg,
99105
string $basePath,
100106
array &$parsedImports,
101-
CircularImportDetector $detector,
107+
CircularImportDetectorInterface $detector,
102108
array &$importedConfigs,
103109
array $importConfig,
104110
): void {
@@ -131,7 +137,7 @@ private function processWildcardImport(
131137

132138
// Create a single import config for this match
133139
$singleImportCfg = new ImportConfig(
134-
path: \ltrim(str_replace($this->dirs->rootPath, '', $matchingPath), '/'), // Use the actual file path
140+
path: \ltrim(\str_replace($this->dirs->rootPath, '', $matchingPath), '/'), // Use the actual file path
135141
absolutePath: $matchingPath, // The path finder returns absolute paths
136142
pathPrefix: $importCfg->pathPrefix,
137143
hasWildcard: false,
@@ -178,11 +184,11 @@ private function processSingleImport(
178184

179185
// Apply path prefix for output document paths if specified
180186
if ($importCfg->pathPrefix !== null) {
181-
$importedConfig = $this->applyPathPrefix($importedConfig, $importCfg->pathPrefix);
187+
$importedConfig = $this->documentPrefixer->applyPrefix($importedConfig, $importCfg->pathPrefix);
182188
}
183189

184-
// Apply path prefix if specified
185-
$importedConfig = $this->applySourcePathPrefix($importedConfig, $importCfg->configDirectory());
190+
// Apply source path prefix
191+
$importedConfig = $this->sourcePrefixer->applyPrefix($importedConfig, $importCfg->configDirectory());
186192

187193
// Store for later merging
188194
$importedConfigs[] = $importedConfig;
@@ -239,108 +245,6 @@ private function loadImportConfig(ImportConfig $importConfig): array
239245
}
240246
}
241247

242-
/**
243-
* Apply path prefix to the output path of all documents
244-
*
245-
* The pathPrefix specifies a subdirectory to prepend to all document outputPath values
246-
* in the imported configuration.
247-
*
248-
* Example:
249-
* - Document has outputPath: 'docs/api.md'
250-
* - Import has pathPrefix: 'api/v1'
251-
* - Resulting outputPath: 'api/v1/docs/api.md'
252-
*
253-
* @param array $config The imported configuration
254-
* @param string $pathPrefix The path prefix to apply
255-
* @return array The modified configuration with path prefix applied to document outputPaths
256-
*/
257-
private function applyPathPrefix(array $config, string $pathPrefix): array
258-
{
259-
// Apply to document outputPath values
260-
if (isset($config['documents']) && \is_array($config['documents'])) {
261-
foreach ($config['documents'] as &$document) {
262-
if (isset($document['outputPath'])) {
263-
$document['outputPath'] = $this->combinePaths($pathPrefix, $document['outputPath']);
264-
}
265-
}
266-
}
267-
268-
return $config;
269-
}
270-
271-
/**
272-
* Apply path prefix to relevant source paths
273-
*/
274-
private function applySourcePathPrefix(array $config, string $pathPrefix): array
275-
{
276-
// Apply to documents array
277-
if (isset($config['documents']) && \is_array($config['documents'])) {
278-
foreach ($config['documents'] as &$document) {
279-
if (isset($document['sources']) && \is_array($document['sources'])) {
280-
foreach ($document['sources'] as &$source) {
281-
$source = $this->applyPathPrefixToSource($source, $pathPrefix);
282-
}
283-
}
284-
}
285-
}
286-
287-
return $config;
288-
}
289-
290-
/**
291-
* Apply path prefix to a source configuration
292-
*/
293-
private function applyPathPrefixToSource(array $source, string $prefix): array
294-
{
295-
// Handle sourcePaths property
296-
if (isset($source['sourcePaths'])) {
297-
$source['sourcePaths'] = $this->applyPrefixToPaths($source['sourcePaths'], $prefix);
298-
}
299-
300-
// Handle composerPath property (for composer source)
301-
if (isset($source['composerPath']) && $source['type'] === 'composer') {
302-
if (!\str_starts_with($source['composerPath'], '/')) {
303-
$source['composerPath'] = $this->combinePaths($prefix, $source['composerPath']);
304-
}
305-
}
306-
307-
return $source;
308-
}
309-
310-
/**
311-
* Apply prefix to path(s)
312-
*/
313-
private function applyPrefixToPaths(mixed $paths, string $prefix): mixed
314-
{
315-
if (\is_string($paths)) {
316-
// Skip absolute paths
317-
if (\str_starts_with($paths, '/')) {
318-
return $paths;
319-
}
320-
return $this->combinePaths($prefix, $paths);
321-
}
322-
323-
if (\is_array($paths)) {
324-
$result = [];
325-
foreach ($paths as $path) {
326-
$result[] = $this->applyPrefixToPaths($path, $prefix);
327-
}
328-
return $result;
329-
}
330-
331-
// If it's not a string or array, return as is
332-
return $paths;
333-
}
334-
335-
336-
/**
337-
* Combine two paths with a separator
338-
*/
339-
private function combinePaths(string $prefix, string $path): string
340-
{
341-
return \rtrim($prefix, '/') . '/' . \ltrim($path, '/');
342-
}
343-
344248
/**
345249
* Merge multiple configurations
346250
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Butschster\ContextGenerator\ConfigLoader\Import\PathPrefixer;
6+
7+
/**
8+
* Applies path prefix to document output paths
9+
*
10+
* The pathPrefix specifies a subdirectory to prepend to all document outputPath values
11+
* in the imported configuration.
12+
*
13+
* Example:
14+
* - Document has outputPath: 'docs/api.md'
15+
* - Import has pathPrefix: 'api/v1'
16+
* - Resulting outputPath: 'api/v1/docs/api.md'
17+
*/
18+
final readonly class DocumentOutputPathPrefixer extends PathPrefixer
19+
{
20+
/**
21+
* Apply path prefix to the output path of all documents
22+
*/
23+
public function applyPrefix(array $config, string $pathPrefix): array
24+
{
25+
// Apply to document outputPath values
26+
if (isset($config['documents']) && \is_array($config['documents'])) {
27+
foreach ($config['documents'] as &$document) {
28+
if (isset($document['outputPath'])) {
29+
$document['outputPath'] = $this->combinePaths($pathPrefix, $document['outputPath']);
30+
}
31+
}
32+
}
33+
34+
return $config;
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Butschster\ContextGenerator\ConfigLoader\Import\PathPrefixer;
6+
7+
/**
8+
* Abstract base class for path prefix operations
9+
*/
10+
abstract readonly class PathPrefixer
11+
{
12+
/**
13+
* Apply a path prefix to the appropriate paths in a configuration
14+
*/
15+
abstract public function applyPrefix(array $config, string $pathPrefix): array;
16+
17+
/**
18+
* Combine two paths with a separator
19+
*/
20+
protected function combinePaths(string $prefix, string $path): string
21+
{
22+
return \rtrim($prefix, '/') . '/' . \ltrim($path, '/');
23+
}
24+
25+
/**
26+
* Check if path is absolute
27+
*/
28+
protected function isAbsolutePath(string $path): bool
29+
{
30+
return \str_starts_with($path, '/');
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Butschster\ContextGenerator\ConfigLoader\Import\PathPrefixer;
6+
7+
/**
8+
* Applies path prefix to source paths
9+
*
10+
* This prefixer handles various source path formats including:
11+
* - sourcePaths property (string or array)
12+
* - composerPath property (for composer source type)
13+
*/
14+
final readonly class SourcePathPrefixer extends PathPrefixer
15+
{
16+
/**
17+
* Apply path prefix to relevant source paths
18+
*/
19+
public function applyPrefix(array $config, string $pathPrefix): array
20+
{
21+
// Apply to documents array
22+
if (isset($config['documents']) && \is_array($config['documents'])) {
23+
foreach ($config['documents'] as &$document) {
24+
if (isset($document['sources']) && \is_array($document['sources'])) {
25+
foreach ($document['sources'] as &$source) {
26+
$source = $this->applyPathPrefixToSource($source, $pathPrefix);
27+
}
28+
}
29+
}
30+
}
31+
32+
return $config;
33+
}
34+
35+
/**
36+
* Apply path prefix to a source configuration
37+
*/
38+
private function applyPathPrefixToSource(array $source, string $prefix): array
39+
{
40+
// Handle sourcePaths property
41+
if (isset($source['sourcePaths'])) {
42+
$source['sourcePaths'] = $this->applyPrefixToPaths($source['sourcePaths'], $prefix);
43+
}
44+
45+
// Handle composerPath property (for composer source)
46+
if (isset($source['composerPath']) && $source['type'] === 'composer') {
47+
if (!$this->isAbsolutePath($source['composerPath'])) {
48+
$source['composerPath'] = $this->combinePaths($prefix, $source['composerPath']);
49+
}
50+
}
51+
52+
return $source;
53+
}
54+
55+
/**
56+
* Apply prefix to path(s)
57+
*/
58+
private function applyPrefixToPaths(mixed $paths, string $prefix): mixed
59+
{
60+
if (\is_string($paths)) {
61+
// Skip absolute paths
62+
if ($this->isAbsolutePath($paths)) {
63+
return $paths;
64+
}
65+
return $this->combinePaths($prefix, $paths);
66+
}
67+
68+
if (\is_array($paths)) {
69+
$result = [];
70+
foreach ($paths as $path) {
71+
$result[] = $this->applyPrefixToPaths($path, $prefix);
72+
}
73+
return $result;
74+
}
75+
76+
// If it's not a string or array, return as is
77+
return $paths;
78+
}
79+
}

tests/src/ConfigLoader/Import/ImportResolverTest.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use Butschster\ContextGenerator\ConfigLoader\ConfigLoaderFactoryInterface;
88
use Butschster\ContextGenerator\ConfigLoader\ConfigLoaderInterface;
9-
use Butschster\ContextGenerator\ConfigLoader\Import\CircularImportDetector;
109
use Butschster\ContextGenerator\ConfigLoader\Import\ImportResolver;
1110
use Butschster\ContextGenerator\Directories;
1211
use Butschster\ContextGenerator\FilesInterface;
@@ -299,7 +298,7 @@ protected function setUp(): void
299298
*/
300299
private function loadFixture(string $filename): array
301300
{
302-
$path = realpath(self::FIXTURES_DIR . '/' . $filename);
301+
$path = \realpath(self::FIXTURES_DIR . '/' . $filename);
303302

304303
if (!\file_exists($path)) {
305304
throw new \RuntimeException("Fixture file not found: {$path}");

0 commit comments

Comments
 (0)