[DEPRECATED] Go implementation of plcbundle
at main 6.4 kB view raw
1package bundle 2 3import ( 4 "fmt" 5 "path/filepath" 6 "time" 7 8 "tangled.org/atscan.net/plcbundle-go/internal/bundleindex" 9 "tangled.org/atscan.net/plcbundle-go/internal/plcclient" 10 "tangled.org/atscan.net/plcbundle-go/internal/types" 11) 12 13// Bundle represents a PLC bundle 14type Bundle struct { 15 BundleNumber int `json:"bundle_number"` 16 StartTime time.Time `json:"start_time"` 17 EndTime time.Time `json:"end_time"` 18 Operations []plcclient.PLCOperation `json:"-"` 19 DIDCount int `json:"did_count"` 20 21 Hash string `json:"hash"` // Chain hash (primary) 22 ContentHash string `json:"content_hash"` // Content hash 23 Parent string `json:"parent"` 24 25 CompressedHash string `json:"compressed_hash"` 26 CompressedSize int64 `json:"compressed_size"` 27 UncompressedSize int64 `json:"uncompressed_size"` 28 Cursor string `json:"cursor"` 29 BoundaryCIDs []string `json:"boundary_cids,omitempty"` 30 Compressed bool `json:"compressed"` 31 CreatedAt time.Time `json:"created_at"` 32} 33 34// GetFilePath returns the file path for this bundle 35func (b *Bundle) GetFilePath(bundleDir string) string { 36 return filepath.Join(bundleDir, fmt.Sprintf("%06d.jsonl.zst", b.BundleNumber)) 37} 38 39// GetUncompressedFilePath returns the uncompressed file path 40func (b *Bundle) GetUncompressedFilePath(bundleDir string) string { 41 return filepath.Join(bundleDir, fmt.Sprintf("%06d.jsonl", b.BundleNumber)) 42} 43 44// OperationCount returns the number of operations (always BUNDLE_SIZE for complete bundles) 45func (b *Bundle) OperationCount() int { 46 if len(b.Operations) > 0 { 47 return len(b.Operations) 48 } 49 return types.BUNDLE_SIZE 50} 51 52// CompressionRatio returns the compression ratio 53func (b *Bundle) CompressionRatio() float64 { 54 if b.CompressedSize == 0 { 55 return 0 56 } 57 return float64(b.UncompressedSize) / float64(b.CompressedSize) 58} 59 60// ValidateForSave performs validation before saving (no hash required) 61func (b *Bundle) ValidateForSave() error { 62 if b.BundleNumber < 1 { 63 return fmt.Errorf("invalid bundle number: %d", b.BundleNumber) 64 } 65 if len(b.Operations) != types.BUNDLE_SIZE { 66 return fmt.Errorf("invalid operation count: expected %d, got %d", types.BUNDLE_SIZE, len(b.Operations)) 67 } 68 if b.StartTime.After(b.EndTime) { 69 return fmt.Errorf("start_time is after end_time") 70 } 71 return nil 72} 73 74// Validate performs full validation (including hashes) 75func (b *Bundle) Validate() error { 76 if err := b.ValidateForSave(); err != nil { 77 return err 78 } 79 if b.Hash == "" { 80 return fmt.Errorf("missing hash") 81 } 82 if b.CompressedHash == "" { 83 return fmt.Errorf("missing compressed hash") 84 } 85 return nil 86} 87 88// VerificationResult contains the result of bundle verification 89type VerificationResult struct { 90 BundleNumber int 91 Valid bool 92 HashMatch bool 93 FileExists bool 94 Error error 95 LocalHash string 96 ExpectedHash string 97} 98 99// ChainVerificationResult contains the result of chain verification 100type ChainVerificationResult struct { 101 Valid bool 102 ChainLength int 103 BrokenAt int 104 Error string 105 VerifiedBundles []int 106} 107 108// DirectoryScanResult contains results from scanning a directory 109type DirectoryScanResult struct { 110 BundleDir string 111 BundleCount int 112 FirstBundle int 113 LastBundle int 114 MissingGaps []int 115 TotalSize int64 // Compressed size 116 TotalUncompressed int64 117 IndexUpdated bool 118} 119 120// Config holds configuration for bundle operations 121type Config struct { 122 BundleDir string 123 HandleResolverURL string 124 VerifyOnLoad bool 125 AutoRebuild bool 126 AutoInit bool // Allow auto-creating empty repository 127 RebuildWorkers int // Number of workers for parallel rebuild (0 = auto-detect) 128 RebuildProgress func(current, total int) // Progress callback for rebuild 129 Logger types.Logger 130 Verbose bool 131 Quiet bool 132} 133 134// DefaultConfig returns default configuration 135func DefaultConfig(bundleDir string) *Config { 136 return &Config{ 137 BundleDir: bundleDir, 138 HandleResolverURL: "https://quickdid.atscan.net", 139 VerifyOnLoad: true, 140 AutoRebuild: true, 141 AutoInit: false, 142 RebuildWorkers: 0, // 0 means auto-detect CPU count 143 RebuildProgress: nil, // No progress callback by default 144 Logger: nil, 145 Verbose: false, 146 Quiet: false, 147 } 148} 149 150// CloneOptions configures cloning behavior 151type CloneOptions struct { 152 RemoteURL string 153 Workers int 154 SkipExisting bool 155 ProgressFunc func(downloaded, total int, bytesDownloaded, bytesTotal int64) 156 SaveInterval time.Duration 157 Verbose bool 158 Logger types.Logger 159} 160 161// CloneResult contains cloning results 162type CloneResult struct { 163 RemoteBundles int 164 Downloaded int 165 Failed int 166 Skipped int 167 TotalBytes int64 168 Duration time.Duration 169 Interrupted bool 170 FailedBundles []int 171} 172 173// PLCOperationWithLocation contains an operation with its bundle/position metadata 174type PLCOperationWithLocation struct { 175 Operation plcclient.PLCOperation 176 Bundle int 177 Position int 178} 179 180func (b *Bundle) ToMetadata() *bundleindex.BundleMetadata { 181 return &bundleindex.BundleMetadata{ 182 BundleNumber: b.BundleNumber, 183 StartTime: b.StartTime, 184 EndTime: b.EndTime, 185 OperationCount: b.OperationCount(), 186 DIDCount: b.DIDCount, 187 Hash: b.Hash, // Chain hash 188 ContentHash: b.ContentHash, // Content hash 189 Parent: b.Parent, 190 CompressedHash: b.CompressedHash, 191 CompressedSize: b.CompressedSize, 192 UncompressedSize: b.UncompressedSize, 193 Cursor: b.Cursor, 194 CreatedAt: b.CreatedAt, 195 } 196} 197 198// ResolveDIDResult contains DID resolution with timing metrics 199type ResolveDIDResult struct { 200 Document *plcclient.DIDDocument 201 LatestOperation *plcclient.PLCOperation 202 MempoolTime time.Duration 203 IndexTime time.Duration 204 LoadOpTime time.Duration 205 TotalTime time.Duration 206 ResolvedHandle string 207 Source string // "mempool" or "bundle" 208 BundleNumber int // if from bundle 209 Position int // if from bundle 210} 211 212type resolverTiming struct { 213 totalTime int64 214 mempoolTime int64 215 indexTime int64 216 loadOpTime int64 217 source string // "mempool" or "bundle" 218}