import { projectBounded, projectBoundedInOrder, type BoundedProjection, } from '../utils/bounded-projection.js'; export interface SelectedHandlerSource { methodId: number; className?: string; methodName?: string; repositoryId?: number; repositoryName?: string; repositoryPackageName?: string; sourceFile?: string; sourceLine?: number; } export interface SelectedHandlerProvenance { status: 'selected'; accepted: true; graphTargetId: string; methodId: number; className?: string; methodName?: string; repository?: { id?: number; name?: string; packageName?: string; }; sourceFile?: string; sourceLine?: number; } export function selectedHandlerProvenance( source: SelectedHandlerSource, ): SelectedHandlerProvenance { return { status: 'selected', accepted: true, graphTargetId: String(source.methodId), methodId: source.methodId, className: source.className, methodName: source.methodName, repository: { id: source.repositoryId, name: source.repositoryName, packageName: source.repositoryPackageName, }, sourceFile: source.sourceFile, sourceLine: source.sourceLine, }; } export function displayImplementationCandidates( candidates: Array>, selectedMethodId: number | undefined, ): Array> { return [...candidates] .sort((left, right) => compareDisplayCandidate( left, right, selectedMethodId, )) .map((candidate, index) => ({ ...candidate, discoveryRank: candidate.rank, displayRank: index + 1, selected: selectedMethodId !== undefined && Number(candidate.methodId) === selectedMethodId, })); } function compareDisplayCandidate( left: Record, right: Record, selectedMethodId: number | undefined, ): number { return Number(Number(right.methodId) === selectedMethodId) - Number(Number(left.methodId) === selectedMethodId) || Number(right.accepted === true) - Number(left.accepted === true) || numberValue(left.rank) - numberValue(right.rank) || compareTargetCandidates(left, right); } export function boundedImplementationEvidence( evidence: Record, targetCandidateCount: number, ): Record { const candidates = recordArray(evidence.candidates); const candidateProjection = projectBoundedInOrder(candidates); const families = recordArray(evidence.candidateFamilies); const familyProjection = projectBounded(families, compareFamilies); const hints = recordArray(evidence.implementationHintSuggestions); const hintProjection = projectBounded(hints, compareHints); const hintCount = Math.max( numberValue(evidence.implementationHintSuggestionCount), hintProjection.totalCount, ); const targets = Math.max(0, targetCandidateCount); return { ...evidence, candidates: candidateProjection.items.map(boundedCandidateEvidence), candidateCount: candidateProjection.totalCount, shownCandidateCount: candidateProjection.shownCount, omittedCandidateCount: candidateProjection.omittedCount, candidateFamilies: familyProjection.items.map(boundedFamilyEvidence), candidateFamilyCount: familyProjection.totalCount, shownCandidateFamilyCount: familyProjection.shownCount, omittedCandidateFamilyCount: familyProjection.omittedCount, implementationHintSuggestions: hintProjection.items, implementationHintSuggestionCount: hintCount, shownImplementationHintSuggestionCount: hintProjection.shownCount, omittedImplementationHintSuggestionCount: Math.max(0, hintCount - hintProjection.shownCount), candidateTargetCount: targets, shownCandidateTargetCount: Math.min(targets, candidateProjection.shownCount), omittedCandidateTargetCount: Math.max(0, targets - candidateProjection.shownCount), }; } export function boundedImplementationTargetIds( candidates: Array>, ): BoundedProjection { const projection = projectBounded(candidates, compareTargetCandidates); return { totalCount: projection.totalCount, shownCount: projection.shownCount, omittedCount: projection.omittedCount, items: projection.items.map((candidate) => String(candidate.methodId ?? '')), }; } function boundedCandidateEvidence(candidate: Record): Record { const registrations = recordArray(candidate.registrations); const projection = projectBounded(registrations, compareRegistrations); return { ...candidate, registrations: projection.items, registrationCount: projection.totalCount, shownRegistrationCount: projection.shownCount, omittedRegistrationCount: projection.omittedCount, }; } function boundedFamilyEvidence(family: Record): Record { const repositories = stringArray(family.repositories); const projection = projectBounded(repositories, (left, right) => left.localeCompare(right)); return { ...family, repositories: projection.items, repositoryCount: projection.totalCount, shownRepositoryCount: projection.shownCount, omittedRepositoryCount: projection.omittedCount, }; } function compareTargetCandidates(left: Record, right: Record): number { return Number(right.score ?? 0) - Number(left.score ?? 0) || String(left.className ?? '').localeCompare(String(right.className ?? '')) || Number(left.methodId ?? 0) - Number(right.methodId ?? 0); } function compareFamilies(left: Record, right: Record): number { return String(left.packageName ?? '').localeCompare(String(right.packageName ?? '')) || String(left.reason ?? '').localeCompare(String(right.reason ?? '')); } function compareHints(left: Record, right: Record): number { return String(left.cli ?? '').localeCompare(String(right.cli ?? '')) || String(left.implementationRepo ?? '').localeCompare(String(right.implementationRepo ?? '')); } function compareRegistrations(left: Record, right: Record): number { return String(left.file ?? '').localeCompare(String(right.file ?? '')) || Number(left.line ?? 0) - Number(right.line ?? 0) || Number(left.id ?? 0) - Number(right.id ?? 0); } function recordArray(value: unknown): Array> { return Array.isArray(value) ? value.filter((item): item is Record => Boolean(item && typeof item === 'object' && !Array.isArray(item))) : []; } function stringArray(value: unknown): string[] { return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : []; } function numberValue(value: unknown): number { return typeof value === 'number' && Number.isFinite(value) ? value : 0; }