mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-02 08:16:57 +00:00
89 lines
2.3 KiB
Go
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
|
|
}
|