[DEPRECATED] Go implementation of plcbundle
at dc3ea2a9325e0eb4b2be54d284fd249cbcf4b9e2 202 lines 6.0 kB view raw
1package bundle 2 3import ( 4 "fmt" 5 "path/filepath" 6 "time" 7 8 "tangled.org/atscan.net/plcbundle/internal/bundleindex" 9 "tangled.org/atscan.net/plcbundle/internal/plcclient" 10 "tangled.org/atscan.net/plcbundle/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 // Uncompressed size (NEW) 117 IndexUpdated bool 118} 119 120// Config holds configuration for bundle operations 121type Config struct { 122 BundleDir string 123 VerifyOnLoad bool 124 AutoRebuild bool 125 AutoInit bool // Allow auto-creating empty repository 126 RebuildWorkers int // Number of workers for parallel rebuild (0 = auto-detect) 127 RebuildProgress func(current, total int) // Progress callback for rebuild 128 Logger types.Logger 129} 130 131// DefaultConfig returns default configuration 132func DefaultConfig(bundleDir string) *Config { 133 return &Config{ 134 BundleDir: bundleDir, 135 VerifyOnLoad: true, 136 AutoRebuild: true, 137 AutoInit: false, 138 RebuildWorkers: 0, // 0 means auto-detect CPU count 139 RebuildProgress: nil, // No progress callback by default 140 Logger: nil, 141 } 142} 143 144// CloneOptions configures cloning behavior 145type CloneOptions struct { 146 RemoteURL string 147 Workers int 148 SkipExisting bool 149 ProgressFunc func(downloaded, total int, bytesDownloaded, bytesTotal int64) 150 SaveInterval time.Duration 151 Verbose bool 152 Logger types.Logger 153} 154 155// CloneResult contains cloning results 156type CloneResult struct { 157 RemoteBundles int 158 Downloaded int 159 Failed int 160 Skipped int 161 TotalBytes int64 162 Duration time.Duration 163 Interrupted bool 164 FailedBundles []int 165} 166 167// PLCOperationWithLocation contains an operation with its bundle/position metadata 168type PLCOperationWithLocation struct { 169 Operation plcclient.PLCOperation 170 Bundle int 171 Position int 172} 173 174func (b *Bundle) ToMetadata() *bundleindex.BundleMetadata { 175 return &bundleindex.BundleMetadata{ 176 BundleNumber: b.BundleNumber, 177 StartTime: b.StartTime, 178 EndTime: b.EndTime, 179 OperationCount: b.OperationCount(), 180 DIDCount: b.DIDCount, 181 Hash: b.Hash, // Chain hash 182 ContentHash: b.ContentHash, // Content hash 183 Parent: b.Parent, 184 CompressedHash: b.CompressedHash, 185 CompressedSize: b.CompressedSize, 186 UncompressedSize: b.UncompressedSize, 187 Cursor: b.Cursor, 188 CreatedAt: b.CreatedAt, 189 } 190} 191 192// ResolveDIDResult contains DID resolution with timing metrics 193type ResolveDIDResult struct { 194 Document *plcclient.DIDDocument 195 MempoolTime time.Duration 196 IndexTime time.Duration 197 LoadOpTime time.Duration 198 TotalTime time.Duration 199 Source string // "mempool" or "bundle" 200 BundleNumber int // if from bundle 201 Position int // if from bundle 202}