package snapshot import ( "bytes" "encoding/json" "fmt" "io" "github.com/klauspost/compress/zstd" ) // Manifest represents the structure of a snapshot's blob manifest type Manifest struct { SnapshotID string `json:"snapshot_id"` Timestamp string `json:"timestamp"` BlobCount int `json:"blob_count"` TotalCompressedSize int64 `json:"total_compressed_size"` Blobs []BlobInfo `json:"blobs"` } // BlobInfo represents information about a single blob in the manifest type BlobInfo struct { Hash string `json:"hash"` CompressedSize int64 `json:"compressed_size"` } // DecodeManifest decodes a manifest from a reader containing compressed JSON func DecodeManifest(r io.Reader) (*Manifest, error) { // Decompress using zstd zr, err := zstd.NewReader(r) if err != nil { return nil, fmt.Errorf("creating zstd reader: %w", err) } defer zr.Close() // Decode JSON manifest var manifest Manifest if err := json.NewDecoder(zr).Decode(&manifest); err != nil { return nil, fmt.Errorf("decoding manifest: %w", err) } return &manifest, nil } // EncodeManifest encodes a manifest to compressed JSON func EncodeManifest(manifest *Manifest, compressionLevel int) ([]byte, error) { // Marshal to JSON jsonData, err := json.MarshalIndent(manifest, "", " ") if err != nil { return nil, fmt.Errorf("marshaling manifest: %w", err) } // Compress using zstd var compressedBuf bytes.Buffer writer, err := zstd.NewWriter(&compressedBuf, zstd.WithEncoderLevel(zstd.EncoderLevelFromZstd(compressionLevel))) if err != nil { return nil, fmt.Errorf("creating zstd writer: %w", err) } if _, err := writer.Write(jsonData); err != nil { _ = writer.Close() return nil, fmt.Errorf("writing compressed data: %w", err) } if err := writer.Close(); err != nil { return nil, fmt.Errorf("closing zstd writer: %w", err) } return compressedBuf.Bytes(), nil }