Files
at-container-registry/pkg/hold/pds/import.go
T

89 lines
2.3 KiB
Go

package pds
import (
"context"
"fmt"
"io"
"strings"
"github.com/bluesky-social/indigo/repo"
"github.com/ipfs/go-cid"
)
// rawCBOR wraps raw bytes to satisfy cbg.CBORMarshaler.
// Used to pass through record bytes from a CAR without decoding.
type rawCBOR []byte
func (r rawCBOR) MarshalCBOR(w io.Writer) error {
_, err := w.Write(r)
return err
}
// ImportResult summarizes a CAR import operation.
type ImportResult struct {
Total int
PerCollection map[string]int
}
// ImportFromCAR reads a CAR file and imports all records into the hold's repo.
// Records are upserted (overwrite on conflict) in a single atomic commit.
// The repo is initialized if it doesn't exist yet.
func (p *HoldPDS) ImportFromCAR(ctx context.Context, r io.Reader) (*ImportResult, error) {
// Ensure repo exists
head, err := p.carstore.GetUserRepoHead(ctx, p.uid)
if err != nil || !head.Defined() {
if err := p.repomgr.InitNewActor(ctx, p.uid, "", p.did, "", "", ""); err != nil {
return nil, fmt.Errorf("failed to initialize repo: %w", err)
}
}
// Parse the CAR into an in-memory repo
sourceRepo, err := repo.ReadRepoFromCar(ctx, r)
if err != nil {
return nil, fmt.Errorf("failed to read CAR: %w", err)
}
// Collect all records
var records []BulkRecord
err = sourceRepo.ForEach(ctx, "", func(k string, v cid.Cid) error {
_, recBytes, err := sourceRepo.GetRecordBytes(ctx, k)
if err != nil {
return fmt.Errorf("failed to get record bytes for %s: %w", k, err)
}
parts := strings.SplitN(k, "/", 2)
if len(parts) != 2 {
return fmt.Errorf("unexpected record path format: %s", k)
}
records = append(records, BulkRecord{
Collection: parts[0],
Rkey: parts[1],
Data: rawCBOR(*recBytes),
})
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to iterate CAR records: %w", err)
}
if len(records) == 0 {
return &ImportResult{PerCollection: map[string]int{}}, nil
}
// Bulk upsert all records in a single commit via the RepoOperator interface
if err := p.repomgr.BulkUpsert(ctx, p.uid, records); err != nil {
return nil, fmt.Errorf("failed to import records: %w", err)
}
// Build result
result := &ImportResult{
Total: len(records),
PerCollection: make(map[string]int),
}
for _, rec := range records {
result.PerCollection[rec.Collection]++
}
return result, nil
}