import type { AggregatedFile } from '../files/index.js' import type { Fragment } from './range_indexer.js' import type { EdgeSegment, FileMeta } from './scan.js' /** * The fragments of one file from neighbouring ranges, put together * (REBUILD.md, fragments): the complete file when every part from 1 to its * total is there (the first article seen of a part wins), else null. */ export function mergeFragments(fragments: Fragment[]): AggregatedFile | null { if (fragments.length === 0) return null const first = fragments[0] const meta: FileMeta = { fileKey: first.fileKey, name: first.name, poster: first.poster, subject: first.subject, totalParts: first.totalParts, groups: first.groups, release: first.release ? { ...first.release, source: first.release.source as any } : null, } const parts = new Map() for (const fragment of fragments) { const postedAt = fragment.postedAt ?? Number.NaN for (const [number, messageId, bytes, articleNumber] of fragment.segments) { const known = parts.get(number) if (!known || articleNumber < known.articleNumber) { parts.set(number, { fileKey: meta.fileKey, number, totalParts: meta.totalParts, messageId, bytes, articleNumber, postedAt, file: meta }) } } } if (parts.size < meta.totalParts) return null for (let n = 1; n <= meta.totalParts; n++) if (!parts.has(n)) return null return { fileKey: meta.fileKey, segments: [...parts.values()].sort((a, b) => a.number - b.number) } }