[DEPRECATED] Go implementation of plcbundle
at rust-test 357 lines 9.4 kB view raw
1package commands 2 3import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 "time" 9 10 "github.com/goccy/go-json" 11 "github.com/spf13/cobra" 12 "tangled.org/atscan.net/plcbundle/internal/types" 13) 14 15func NewMempoolCommand() *cobra.Command { 16 cmd := &cobra.Command{ 17 Use: "mempool [status]", 18 Aliases: []string{"mp"}, 19 Short: "Manage mempool operations", 20 Long: `Manage mempool operations 21 22The mempool stores operations waiting to be bundled. It maintains 23strict chronological order and automatically validates consistency.`, 24 25 Example: ` # Show mempool status 26 plcbundle mempool 27 plcbundle mempool status 28 29 # Clear all operations 30 plcbundle mempool clear 31 32 # Export operations as JSONL 33 plcbundle mempool dump 34 plcbundle mempool dump > operations.jsonl 35 36 # Using alias 37 plcbundle mp status`, 38 39 RunE: func(cmd *cobra.Command, args []string) error { 40 // Default to status subcommand 41 return mempoolStatus(cmd, args) 42 }, 43 } 44 45 // Add subcommands 46 cmd.AddCommand(newMempoolStatusCommand()) 47 cmd.AddCommand(newMempoolClearCommand()) 48 cmd.AddCommand(newMempoolDumpCommand()) 49 50 return cmd 51} 52 53// ============================================================================ 54// MEMPOOL STATUS - Show current state 55// ============================================================================ 56 57func newMempoolStatusCommand() *cobra.Command { 58 var verbose bool 59 60 cmd := &cobra.Command{ 61 Use: "status", 62 Aliases: []string{"s", "info"}, 63 Short: "Show mempool status", 64 Long: `Show mempool status and statistics 65 66Displays current mempool state including operation count, progress toward 67next bundle, validation status, and memory usage.`, 68 69 Example: ` # Show status 70 plcbundle mempool status 71 plcbundle mempool 72 73 # Verbose output with samples 74 plcbundle mempool status -v`, 75 76 RunE: func(cmd *cobra.Command, args []string) error { 77 return mempoolStatus(cmd, args) 78 }, 79 } 80 81 cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Show sample operations") 82 83 return cmd 84} 85 86func mempoolStatus(cmd *cobra.Command, _ []string) error { 87 verbose, _ := cmd.Flags().GetBool("verbose") 88 if cmd.Parent() != nil { 89 // Called as subcommand, check parent's verbose flag 90 if v, err := cmd.Parent().Flags().GetBool("verbose"); err == nil && v { 91 verbose = true 92 } 93 } 94 95 mgr, dir, err := getManager(&ManagerOptions{Cmd: cmd}) 96 if err != nil { 97 return err 98 } 99 defer mgr.Close() 100 101 return showMempoolStatus(mgr, dir, verbose) 102} 103 104// ============================================================================ 105// MEMPOOL CLEAR - Remove all operations 106// ============================================================================ 107 108func newMempoolClearCommand() *cobra.Command { 109 var force bool 110 111 cmd := &cobra.Command{ 112 Use: "clear", 113 Aliases: []string{"c", "reset"}, 114 Short: "Clear mempool operations", 115 Long: `Clear all operations from mempool 116 117Removes all operations from the mempool and saves the empty state. 118This is a destructive operation that cannot be undone. 119 120Use cases: 121 • Reset after testing 122 • Clear corrupted data 123 • Force fresh start`, 124 125 Example: ` # Clear with confirmation 126 plcbundle mempool clear 127 128 # Force clear without confirmation 129 plcbundle mempool clear --force 130 plcbundle mempool clear -f`, 131 132 RunE: func(cmd *cobra.Command, args []string) error { 133 mgr, dir, err := getManager(&ManagerOptions{Cmd: cmd}) 134 if err != nil { 135 return err 136 } 137 defer mgr.Close() 138 139 stats := mgr.GetMempoolStats() 140 count := stats["count"].(int) 141 142 if count == 0 { 143 fmt.Println("Mempool is already empty") 144 return nil 145 } 146 147 fmt.Printf("Working in: %s\n\n", dir) 148 149 if !force { 150 fmt.Printf("⚠️ This will clear %d operations from the mempool.\n", count) 151 fmt.Printf("Are you sure? [y/N]: ") 152 var response string 153 fmt.Scanln(&response) 154 if strings.ToLower(strings.TrimSpace(response)) != "y" { 155 fmt.Println("Cancelled") 156 return nil 157 } 158 } 159 160 if err := mgr.ClearMempool(); err != nil { 161 return fmt.Errorf("clear failed: %w", err) 162 } 163 164 fmt.Printf("\n✓ Mempool cleared (%d operations removed)\n", count) 165 return nil 166 }, 167 } 168 169 cmd.Flags().BoolVarP(&force, "force", "f", false, "Skip confirmation prompt") 170 171 return cmd 172} 173 174// ============================================================================ 175// MEMPOOL DUMP - Export operations as JSONL 176// ============================================================================ 177 178func newMempoolDumpCommand() *cobra.Command { 179 var outputFile string 180 181 cmd := &cobra.Command{ 182 Use: "dump", 183 Aliases: []string{"export", "d"}, 184 Short: "Export mempool as JSONL", 185 Long: `Export mempool operations as JSONL 186 187Outputs all operations in the mempool as newline-delimited JSON. 188Perfect for backup, analysis, or piping to other tools.`, 189 190 Example: ` # Dump to stdout 191 plcbundle mempool dump 192 193 # Save to file 194 plcbundle mempool dump > mempool.jsonl 195 plcbundle mempool dump -o mempool.jsonl 196 197 # Pipe to jq 198 plcbundle mempool dump | jq -r .did 199 200 # Count operations 201 plcbundle mempool dump | wc -l 202 203 # Using alias 204 plcbundle mempool export`, 205 206 RunE: func(cmd *cobra.Command, args []string) error { 207 mgr, _, err := getManager(&ManagerOptions{Cmd: cmd}) 208 if err != nil { 209 return err 210 } 211 defer mgr.Close() 212 213 ops, err := mgr.GetMempoolOperations() 214 if err != nil { 215 return fmt.Errorf("failed to get mempool operations: %w", err) 216 } 217 218 if len(ops) == 0 { 219 fmt.Fprintf(os.Stderr, "Mempool is empty\n") 220 return nil 221 } 222 223 // Determine output destination 224 var output *os.File 225 if outputFile != "" { 226 output, err = os.Create(outputFile) 227 if err != nil { 228 return fmt.Errorf("failed to create output file: %w", err) 229 } 230 defer output.Close() 231 fmt.Fprintf(os.Stderr, "Exporting to: %s\n", outputFile) 232 } else { 233 output = os.Stdout 234 } 235 236 // Write JSONL 237 for _, op := range ops { 238 if len(op.RawJSON) > 0 { 239 output.Write(op.RawJSON) 240 } else { 241 data, _ := json.Marshal(op) 242 output.Write(data) 243 } 244 output.Write([]byte("\n")) 245 } 246 247 fmt.Fprintf(os.Stderr, "Exported %d operations from mempool\n", len(ops)) 248 return nil 249 }, 250 } 251 252 cmd.Flags().StringVarP(&outputFile, "output", "o", "", "Output file (default: stdout)") 253 254 return cmd 255} 256 257// ============================================================================ 258// HELPER FUNCTIONS 259// ============================================================================ 260 261func showMempoolStatus(mgr BundleManager, dir string, verbose bool) error { 262 stats := mgr.GetMempoolStats() 263 count := stats["count"].(int) 264 canCreate := stats["can_create_bundle"].(bool) 265 targetBundle := stats["target_bundle"].(int) 266 minTimestamp := stats["min_timestamp"].(time.Time) 267 validated := stats["validated"].(bool) 268 269 fmt.Printf("Mempool Status\n") 270 fmt.Printf("══════════════\n\n") 271 fmt.Printf(" Directory: %s\n", dir) 272 fmt.Printf(" Target bundle: %06d\n", targetBundle) 273 fmt.Printf(" Operations: %d / %d\n", count, types.BUNDLE_SIZE) 274 fmt.Printf(" Min timestamp: %s\n\n", minTimestamp.Format("2006-01-02 15:04:05")) 275 276 // Validation status 277 validationIcon := "✓" 278 if !validated { 279 validationIcon = "⚠️" 280 } 281 fmt.Printf(" Validated: %s %v\n", validationIcon, validated) 282 283 if count > 0 { 284 // Size information 285 if sizeBytes, ok := stats["size_bytes"].(int); ok { 286 fmt.Printf(" Size: %.2f KB\n", float64(sizeBytes)/1024) 287 } 288 289 // Time range 290 if firstTime, ok := stats["first_time"].(time.Time); ok { 291 fmt.Printf(" First op: %s\n", firstTime.Format("2006-01-02 15:04:05")) 292 } 293 if lastTime, ok := stats["last_time"].(time.Time); ok { 294 fmt.Printf(" Last op: %s\n", lastTime.Format("2006-01-02 15:04:05")) 295 } 296 297 fmt.Printf("\n") 298 299 // Progress bar 300 progress := float64(count) / float64(types.BUNDLE_SIZE) * 100 301 fmt.Printf(" Progress: %.1f%% (%d/%d)\n", progress, count, types.BUNDLE_SIZE) 302 303 barWidth := 40 304 filled := int(float64(barWidth) * float64(count) / float64(types.BUNDLE_SIZE)) 305 if filled > barWidth { 306 filled = barWidth 307 } 308 bar := strings.Repeat("█", filled) + strings.Repeat("░", barWidth-filled) 309 fmt.Printf(" [%s]\n\n", bar) 310 311 // Bundle creation status 312 if canCreate { 313 fmt.Printf(" ✓ Ready to create bundle\n") 314 } else { 315 remaining := types.BUNDLE_SIZE - count 316 fmt.Printf(" Need %s more operations\n", formatNumber(remaining)) 317 } 318 } else { 319 fmt.Printf("\n (empty)\n") 320 } 321 322 fmt.Printf("\n") 323 324 // Verbose: Show sample operations 325 if verbose && count > 0 { 326 fmt.Printf("Sample Operations (first 10)\n") 327 fmt.Printf("────────────────────────────\n\n") 328 329 ops, err := mgr.GetMempoolOperations() 330 if err != nil { 331 return fmt.Errorf("error getting operations: %w", err) 332 } 333 334 showCount := 10 335 if len(ops) < showCount { 336 showCount = len(ops) 337 } 338 339 for i := 0; i < showCount; i++ { 340 op := ops[i] 341 fmt.Printf(" %d. DID: %s\n", i+1, op.DID) 342 fmt.Printf(" CID: %s\n", op.CID) 343 fmt.Printf(" Created: %s\n", op.CreatedAt.Format("2006-01-02 15:04:05.000")) 344 fmt.Printf("\n") 345 } 346 347 if len(ops) > showCount { 348 fmt.Printf(" ... and %d more\n\n", len(ops)-showCount) 349 } 350 } 351 352 // Show mempool file location 353 mempoolFilename := fmt.Sprintf("plc_mempool_%06d.jsonl", targetBundle) 354 fmt.Printf("File: %s\n", filepath.Join(dir, mempoolFilename)) 355 356 return nil 357}