package common import ( "fmt" "regexp" "strconv" "strings" "time" "igit.com/xbase/raft" ) const ( ColorReset = "\033[0m" ColorDim = "\033[90m" // Dark Gray ColorRed = "\033[31m" ColorGreen = "\033[32m" ColorYellow = "\033[33m" ColorBlue = "\033[34m" ColorCyan = "\033[36m" ) // Helper to calculate visible length of string ignoring ANSI codes var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`) func visibleLen(s string) int { clean := ansiRegex.ReplaceAllString(s, "") return len([]rune(clean)) } func printBoxed(content string) { lines := strings.Split(strings.TrimSpace(content), "\n") maxWidth := 0 for _, line := range lines { l := visibleLen(line) if l > maxWidth { maxWidth = l } } // Min width to look decent if maxWidth < 20 { maxWidth = 20 } // Add padding contentWidth := maxWidth + 2 fmt.Println() // Start new line before box // Top Border fmt.Printf("%s╭%s╮%s\n", ColorDim, strings.Repeat("─", contentWidth), ColorReset) // Content for _, line := range lines { visLen := visibleLen(line) padding := contentWidth - visLen - 1 // -1 for the space after │ // Ensure padding is not negative if padding < 0 { padding = 0 } fmt.Printf("%s│%s %s%s%s│%s\n", ColorDim, ColorReset, line, strings.Repeat(" ", padding), ColorDim, ColorReset) } // Bottom Border fmt.Printf("%s╰%s╯%s\n", ColorDim, strings.Repeat("─", contentWidth), ColorReset) fmt.Println() // End new line after box } // RegisterDemoCommands registers the demo commands (demodata, deletedatas) to the CLI func RegisterDemoCommands(cli *raft.CLI) { cli.RegisterCommand("demodata", "Generate n items (e.g. 'demodata 100 user.*')", func(parts []string, server *raft.KVServer) { if len(parts) != 3 { printBoxed("Usage: demodata (e.g. demodata 100 user.*)") return } count, err := strconv.Atoi(parts[1]) if err != nil { printBoxed(fmt.Sprintf("Invalid count: %v", err)) return } pattern := parts[2] fmt.Printf("Generating %d items with pattern '%s'...\n", count, pattern) start := time.Now() success := 0 for i := 1; i <= count; i++ { key := strings.Replace(pattern, "*", strconv.Itoa(i), -1) val := fmt.Sprintf("val-%s", key) // Simple value derivation if err := server.Set(key, val); err != nil { fmt.Printf("Failed at %d: %v\n", i, err) } else { success++ } if i%100 == 0 { fmt.Printf("Progress: %d/%d\r", i, count) } } duration := time.Since(start) printBoxed(fmt.Sprintf("%sDone!%s inserted %d/%d items in %v (Avg: %v/op)", ColorGreen, ColorReset, success, count, duration, duration/time.Duration(count))) }) cli.RegisterCommand("deletedatas", "Batch delete n items matching pattern", func(parts []string, server *raft.KVServer) { // deletedatas if len(parts) != 3 { printBoxed("Usage: deletedatas ") return } n, err := strconv.Atoi(parts[1]) if err != nil { printBoxed("Invalid number: " + parts[1]) return } pattern := parts[2] // Replace * with sequential numbers 1..n basePattern := strings.TrimSuffix(pattern, "*") successCount := 0 start := time.Now() fmt.Printf("Deleting up to %d keys matching %s...\n", n, pattern) for i := 1; i <= n; i++ { key := fmt.Sprintf("%s%d", basePattern, i) if err := server.Del(key); err == nil { successCount++ } if i % 100 == 0 { fmt.Print(".") } } fmt.Println() printBoxed(fmt.Sprintf("%sDeleted %d keys in %v%s", ColorGreen, successCount, time.Since(start), ColorReset)) }) }