Skip to main content
Glama

CTX: Context as Code (CaC) tool

by context-hub
MIT License
235
  • Apple
  • Linux
GitDiffSourceFetcher.php8.56 kB
<?php declare(strict_types=1); namespace Butschster\ContextGenerator\Source\GitDiff; use Butschster\ContextGenerator\Application\Logger\LoggerPrefix; use Butschster\ContextGenerator\Lib\Content\ContentBuilderFactory; use Butschster\ContextGenerator\Lib\Finder\FinderResult; use Butschster\ContextGenerator\Modifier\ModifiersApplierInterface; use Butschster\ContextGenerator\Source\Fetcher\SourceFetcherInterface; use Butschster\ContextGenerator\Source\GitDiff\RenderStrategy\Enum\RenderStrategyEnum; use Butschster\ContextGenerator\Source\GitDiff\RenderStrategy\RenderStrategyFactory; use Butschster\ContextGenerator\Source\GitDiff\RenderStrategy\RenderStrategyInterface; use Butschster\ContextGenerator\Source\SourceInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Finder\SplFileInfo; /** * Fetcher for git commit diffs * @implements SourceFetcherInterface<GitDiffSource> */ final readonly class GitDiffSourceFetcher implements SourceFetcherInterface { public function __construct( private GitDiffFinder $finder, private ContentBuilderFactory $builderFactory = new ContentBuilderFactory(), private RenderStrategyFactory $renderStrategyFactory = new RenderStrategyFactory(), #[LoggerPrefix(prefix: 'commit-diff-source')] private ?LoggerInterface $logger = null, ) {} public function supports(SourceInterface $source): bool { $isSupported = $source instanceof GitDiffSource; $this->logger?->debug('Checking if source is supported', [ 'sourceType' => $source::class, 'isSupported' => $isSupported, ]); return $isSupported; } public function fetch(SourceInterface $source, ModifiersApplierInterface $modifiersApplier): string { if (!$source instanceof GitDiffSource) { $errorMessage = 'Source must be an instance of GitDiffSource'; $this->logger?->error($errorMessage, [ 'sourceType' => $source::class, ]); throw new \InvalidArgumentException($errorMessage); } $this->logger?->info('Fetching git diff source content', [ 'description' => $source->getDescription(), 'repository' => $source->repository, 'commit' => $source->commit, 'hasModifiers' => !empty($source->modifiers), ]); // Use the finder to get the diffs $this->logger?->debug('Finding git diffs', [ 'repository' => $source->repository, 'commit' => $source->commit, ]); try { $finderResult = $this->finder->find($source); // Extract diffs from the finder result $this->logger?->debug('Extracting diffs from finder result'); $diffs = $this->extractDiffsFromFinderResult($finderResult); $this->logger?->debug('Diffs extracted', ['diffCount' => \count($diffs)]); // Format the output using the specified render strategy $this->logger?->debug('Using render strategy to format output', [ 'strategy' => $source->renderConfig->strategy, ]); $content = $this->renderOutput($diffs, $finderResult->treeView, $source); $this->logger?->info('Git diff source content fetched successfully', [ 'diffCount' => \count($diffs), 'contentLength' => \strlen($content), 'renderStrategy' => $source->renderConfig->strategy, ]); return $content; } catch (\Throwable $e) { $this->logger?->error('Error fetching git diff content', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); throw $e; } } /** * Extract diffs from the finder result * * @return array<string, array{file: string, diff: string, stats: string}> */ private function extractDiffsFromFinderResult(FinderResult $finderResult): array { $diffs = []; $fileCount = $finderResult->count(); $this->logger?->debug('Processing files for diff extraction', ['fileCount' => $fileCount]); foreach ($finderResult->files as $index => $file) { if (!$file instanceof SplFileInfo) { $this->logger?->warning('Skipping non-SplFileInfo file', [ 'fileType' => $file::class, 'index' => $index, ]); continue; } // Get the original path and diff content $originalPath = \method_exists($file, 'getOriginalPath') ? $file->getOriginalPath() : $file->getRelativePathname(); $this->logger?->debug('Processing diff file', [ 'file' => $originalPath, 'index' => $index + 1, 'total' => $fileCount, ]); $diffContent = $file->getContents(); // Get the stats for this file $stats = ''; if (\method_exists($file, 'getStats')) { $this->logger?->debug('Getting stats from file method'); $stats = $file->getStats(); } else { $this->logger?->debug('Extracting stats from diff content'); // Try to extract stats from the diff content \preg_match('/^(.*?)(?=diff --git)/s', $diffContent, $matches); if (!empty($matches[1])) { $stats = \trim($matches[1]); $this->logger?->debug('Stats extracted from diff content'); } else { $this->logger?->debug('No stats found in diff content'); } } $diffs[$originalPath] = [ 'file' => $originalPath, 'diff' => $diffContent, 'stats' => $stats, ]; $this->logger?->debug('Diff processed', [ 'file' => $originalPath, 'diffLength' => \strlen($diffContent), 'hasStats' => !empty($stats), ]); } $this->logger?->debug('All diffs extracted', ['diffCount' => \count($diffs)]); return $diffs; } /** * Render the diffs using the specified strategy * * @param array<string, array{file: string, diff: string, stats: string}> $diffs */ private function renderOutput(array $diffs, string $treeView, GitDiffSource $source): string { $this->logger?->debug('Creating content builder'); $builder = $this->builderFactory->create(); try { // Handle empty diffs case if (empty($diffs)) { $this->logger?->info('No diffs found for commit range', ['commit' => $source->commit]); $builder ->addTitle("Git Diff for Commit Range: {$source->commit}", 1) ->addText("No changes found in this commit range."); return $builder->build(); } $builder ->addTitle("Git Diff for Commit Range: {$source->commit}", 1) ->addTitle($source->getDescription(), 2) ->addTitle("Summary of Changes", 2) ->addTreeView($treeView); // Get the appropriate render strategy $strategy = $this->getRenderStrategy($source->renderConfig->strategy); // Use the strategy to render the output $content = $builder ->merge($strategy->render($diffs, $source->renderConfig)) ->build(); $this->logger?->debug('Output rendered using strategy', [ 'contentLength' => \strlen($content), ]); return $content; } catch (\Throwable $e) { // Log the error and fall back to raw rendering if there's an issue with the strategy $this->logger?->error('Error using render strategy, falling back to raw rendering', [ 'strategy' => $source->renderConfig->strategy, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); return $builder->build(); } } /** * Get the appropriate render strategy for the given strategy name */ private function getRenderStrategy(RenderStrategyEnum $strategy): RenderStrategyInterface { return $this->renderStrategyFactory->create($strategy); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/context-hub/generator'

If you have feedback or need assistance with the MCP directory API, please join our Discord server