github.com/seeker-insurance/kit@v0.0.13/pretty/text/cmd/agg/main.go (about) 1 package main 2 3 // TODO(kr): tests 4 5 import ( 6 "bufio" 7 "fmt" 8 "log" 9 "math/rand" 10 "os" 11 "strings" 12 "time" 13 ) 14 15 type agg interface { 16 merge(string) 17 String() string 18 } 19 20 var ( 21 key = 0 22 funcmap = make(map[int]func(init, arg string) agg) 23 argmap = make(map[int]string) 24 symtab = map[string]func(init, arg string) agg{ 25 "first": first, 26 "last": last, 27 "prefix": prefix, 28 "sample": sample, 29 "join": join, 30 "smin": smin, 31 "smax": smax, 32 "min": min, 33 "max": max, 34 "sum": sum, 35 "mean": mean, 36 "count": count, 37 "const": constf, 38 "drop": nil, 39 } 40 ) 41 42 func main() { 43 log.SetPrefix("agg: ") 44 log.SetFlags(0) 45 rand.Seed(time.Now().UnixNano()) 46 for i, sym := range os.Args[1:] { 47 if p := strings.IndexByte(sym, ':'); p >= 0 { 48 sym, argmap[i] = sym[:p], sym[p+1:] 49 } 50 if sym == "key" { 51 key, sym = i, "first" 52 } 53 f, ok := symtab[sym] 54 if !ok { 55 log.Fatalf("bad function: %q", sym) 56 } 57 funcmap[i] = f 58 } 59 60 sc := bufio.NewScanner(os.Stdin) 61 var g *group 62 for sc.Scan() { 63 ss := strings.Fields(sc.Text()) 64 if !matches(g, ss) { 65 emit(g) 66 g = &group{key: ss[key]} 67 } 68 mergeLine(g, ss) 69 } 70 emit(g) 71 } 72 73 type group struct { 74 key string 75 agg []agg 76 } 77 78 func matches(g *group, ss []string) bool { 79 return g != nil && g.key == ss[key] 80 } 81 82 func emit(g *group) { 83 if g == nil { 84 return 85 } 86 rest := false 87 for i, a := range g.agg { 88 if f, ok := funcmap[i]; ok && f == nil { 89 continue 90 } 91 if rest { 92 fmt.Print("\t") 93 } 94 rest = true 95 fmt.Print(a) 96 } 97 fmt.Println() 98 } 99 100 func mergeLine(g *group, ss []string) { 101 for i, s := range ss { 102 if i >= len(g.agg) { 103 f := funcmap[i] 104 if f == nil { 105 f = first 106 } 107 g.agg = append(g.agg, f(s, argmap[i])) 108 } else { 109 g.agg[i].merge(s) 110 } 111 } 112 }