Skip to content

Enforce a non-empty FilePair invariant #85

Description

@woutervanranst

Problem statement

internal sealed record FilePair
{
    /// <summary>
    /// The relative path to the BinaryFile
    /// </summary>
    public required RelativePath RelativePath { get; init; }

    public BinaryFile? Binary { get; init; }

    public PointerFile? Pointer { get; init; }
}

Line 20-22 currently allows Binary and Pointer to both be null, which creates an invalid archive item state. Please fail fast at creation (constructor/factory guard) so invalid pairs cannot enter hashing/upload flow.

That creates duplicate path state inside the same model:

  • for binary-only pairs, RelativePath mirrors BinaryFile.Path
  • for pointer-only pairs, RelativePath is the logical binary path while PointerFile.Path is the physical pointer-file path
  • for binary-plus-pointer pairs, the relationship between all three paths is implied rather than enforced by the type itself

This duplication makes the canonical path an explicit field instead of a derived domain concept, and it leaves room for inconsistent states if future callers construct mismatched values.

Improvement

Evaluate whether FilePair.RelativePath should become a derived property instead of stored state.

Possible direction:

  • infer the canonical repository path from BinaryFile.Path when a binary exists
  • otherwise infer it from PointerFile.Path.ToBinaryPath()
  • change factories to accept only the component models:
    • FromBinary(BinaryFile binary)
    • FromPointer(PointerFile pointer)
    • FromBinaryWithPointer(BinaryFile binary, PointerFile pointer)
  • validate in FromBinaryWithPointer(...) that the pointer resolves back to the binary path

Future enhancement

This is a cleanup/design improvement rather than a required fix.

If implemented later, it should:

  • preserve the current logical path behavior for pointer-only pairs
  • keep call sites simple in the archive pipeline
  • add tests that prove mismatched binary/pointer paths are rejected
  • confirm whether any downstream code still needs access to the physical pointer-file path separately from the canonical repository path

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions