From 9451addc1eba1c34119b2aef8af7dbfbc4cbc354 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Thu, 1 Oct 2026 20:56:59 +0100 Subject: [PATCH 1/2] feat: add SpecFromDigest constructor SpecFromDigest(digest, size) specifies a blob by its digest, pairing with SpecFromDigestCode(code, size). Callers that have a digest and a size no longer build a Blob just to pass it to SpecFromBlob, which now delegates to it. Co-Authored-By: Claude Opus 5.5 (1M context) --- commands/blob/codec.go | 16 +++++++++++----- commands/blob/spec_test.go | 3 ++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/commands/blob/codec.go b/commands/blob/codec.go index febaebc..36453c7 100644 --- a/commands/blob/codec.go +++ b/commands/blob/codec.go @@ -25,16 +25,22 @@ var ( errBothVariants = errors.New("InvalidUnion", "union holds both variants") ) -// SpecFromBlob returns the spec of a blob whose digest is known. Its digest -// code is the digest's own; a digest that is not a multihash has code 0. -func SpecFromBlob(b Blob) BlobSpec { - s := BlobSpec{digest: b.Digest, size: b.Size, valid: true} - if d, err := multihash.Decode(b.Digest); err == nil { +// SpecFromDigest returns the spec of a blob of size bytes whose digest is +// known. Its digest code is the digest's own; a digest that is not a multihash +// has code 0. +func SpecFromDigest(digest multihash.Multihash, size uint64) BlobSpec { + s := BlobSpec{digest: digest, size: size, valid: true} + if d, err := multihash.Decode(digest); err == nil { s.code = d.Code } return s } +// SpecFromBlob returns the spec of a blob whose digest is known. +func SpecFromBlob(b Blob) BlobSpec { + return SpecFromDigest(b.Digest, b.Size) +} + // SpecFromDigestCode returns the spec of a blob of size bytes whose digest is // to be computed with the multihash function code. func SpecFromDigestCode(code, size uint64) BlobSpec { diff --git a/commands/blob/spec_test.go b/commands/blob/spec_test.go index 629aded..e617f29 100644 --- a/commands/blob/spec_test.go +++ b/commands/blob/spec_test.go @@ -56,7 +56,8 @@ func TestBlobSpecRoundTrip(t *testing.T) { func TestBlobSpecAccessors(t *testing.T) { digest := testutil.RandomMultihash(t) - byDigest := blob.SpecFromBlob(blob.Blob{Digest: digest, Size: 7}) + byDigest := blob.SpecFromDigest(digest, 7) + require.Equal(t, blob.SpecFromBlob(blob.Blob{Digest: digest, Size: 7}), byDigest, "a Blob specifies the same blob") got, ok := byDigest.Digest() require.True(t, ok) require.Equal(t, digest, got) From 28089c576cbaea5f52a85dbbda18aa13cf065bca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:02:03 +0000 Subject: [PATCH 2/2] docs: mention SpecFromDigest in BlobSpec docs Co-authored-by: alanshaw <152863+alanshaw@users.noreply.github.com> --- commands/blob/types.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/blob/types.go b/commands/blob/types.go index 5a439b9..112c2cd 100644 --- a/commands/blob/types.go +++ b/commands/blob/types.go @@ -21,10 +21,10 @@ type Blob struct { // compute it as the data streams. A spec naming a digest has the digest's // code too. // -// Construct one with [SpecFromBlob] or [SpecFromDigestCode]. Encoding the zero -// value fails, as does decoding a spec that names both a digest and a digest -// code, or neither. The codec is hand-written (codec.go), over the generated -// codec of [BlobSpecModel]. +// Construct one with [SpecFromBlob], [SpecFromDigest] or [SpecFromDigestCode]. +// Encoding the zero value fails, as does decoding a spec that names both a +// digest and a digest code, or neither. The codec is hand-written (codec.go), +// over the generated codec of [BlobSpecModel]. type BlobSpec struct { digest multihash.Multihash code uint64