github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/cmd/test/info/internal/build_csv/main.go (about) 1 // Package main provides utilities for the info test command. 2 package main 3 4 import ( 5 "encoding/csv" 6 "encoding/json" 7 "flag" 8 "fmt" 9 "io" 10 "log" 11 "os" 12 "sort" 13 "strconv" 14 15 "github.com/rclone/rclone/cmd/test/info/internal" 16 ) 17 18 func main() { 19 fOut := flag.String("o", "out.csv", "Output file") 20 flag.Parse() 21 22 args := flag.Args() 23 remotes := make([]internal.InfoReport, 0, len(args)) 24 for _, fn := range args { 25 f, err := os.Open(fn) 26 if err != nil { 27 log.Fatalf("Unable to open %q: %s", fn, err) 28 } 29 var remote internal.InfoReport 30 dec := json.NewDecoder(f) 31 err = dec.Decode(&remote) 32 if err != nil { 33 log.Fatalf("Unable to decode %q: %s", fn, err) 34 } 35 if remote.ControlCharacters == nil { 36 log.Printf("Skipping remote %s: no ControlCharacters", remote.Remote) 37 } else { 38 remotes = append(remotes, remote) 39 } 40 if err := f.Close(); err != nil { 41 log.Fatalf("Closing %q failed: %s", fn, err) 42 } 43 } 44 45 charsMap := make(map[string]string) 46 var remoteNames []string 47 for _, r := range remotes { 48 remoteNames = append(remoteNames, r.Remote) 49 for k, v := range *r.ControlCharacters { 50 v.Text = k 51 quoted := strconv.Quote(k) 52 charsMap[k] = quoted[1 : len(quoted)-1] 53 } 54 } 55 sort.Strings(remoteNames) 56 57 chars := make([]string, 0, len(charsMap)) 58 for k := range charsMap { 59 chars = append(chars, k) 60 } 61 sort.Strings(chars) 62 63 // char remote output 64 recordsMap := make(map[string]map[string][]string) 65 // remote output 66 hRemoteMap := make(map[string][]string) 67 hOperation := []string{"Write", "Write", "Write", "Get", "Get", "Get", "List", "List", "List"} 68 hPosition := []string{"L", "M", "R", "L", "M", "R", "L", "M", "R"} 69 70 // remote 71 // write get list 72 // left middle right left middle right left middle right 73 74 for _, r := range remotes { 75 hRemoteMap[r.Remote] = []string{r.Remote, "", "", "", "", "", "", "", ""} 76 for k, v := range *r.ControlCharacters { 77 cMap, ok := recordsMap[k] 78 if !ok { 79 cMap = make(map[string][]string, 1) 80 recordsMap[k] = cMap 81 } 82 83 cMap[r.Remote] = []string{ 84 sok(v.WriteError[internal.PositionLeft]), sok(v.WriteError[internal.PositionMiddle]), sok(v.WriteError[internal.PositionRight]), 85 sok(v.GetError[internal.PositionLeft]), sok(v.GetError[internal.PositionMiddle]), sok(v.GetError[internal.PositionRight]), 86 pok(v.InList[internal.PositionLeft]), pok(v.InList[internal.PositionMiddle]), pok(v.InList[internal.PositionRight]), 87 } 88 } 89 } 90 91 records := [][]string{ 92 {"", ""}, 93 {"", ""}, 94 {"Bytes", "Char"}, 95 } 96 for _, r := range remoteNames { 97 records[0] = append(records[0], hRemoteMap[r]...) 98 records[1] = append(records[1], hOperation...) 99 records[2] = append(records[2], hPosition...) 100 } 101 for _, c := range chars { 102 k := charsMap[c] 103 row := []string{fmt.Sprintf("%X", c), k} 104 for _, r := range remoteNames { 105 if m, ok := recordsMap[c][r]; ok { 106 row = append(row, m...) 107 } else { 108 row = append(row, "", "", "", "", "", "", "", "", "") 109 } 110 } 111 records = append(records, row) 112 } 113 114 var writer io.Writer 115 if *fOut == "-" { 116 writer = os.Stdout 117 } else { 118 f, err := os.Create(*fOut) 119 if err != nil { 120 log.Fatalf("Unable to create %q: %s", *fOut, err) 121 } 122 defer func() { 123 if err := f.Close(); err != nil { 124 log.Fatalln("Error writing csv:", err) 125 } 126 }() 127 writer = f 128 } 129 130 w := csv.NewWriter(writer) 131 err := w.WriteAll(records) 132 if err != nil { 133 log.Fatalln("Error writing csv:", err) 134 } else if err := w.Error(); err != nil { 135 log.Fatalln("Error writing csv:", err) 136 } 137 } 138 139 func sok(s string) string { 140 if s != "" { 141 return "ERR" 142 } 143 return "OK" 144 } 145 146 func pok(p internal.Presence) string { 147 switch p { 148 case internal.Absent: 149 return "MIS" 150 case internal.Present: 151 return "OK" 152 case internal.Renamed: 153 return "REN" 154 case internal.Multiple: 155 return "MUL" 156 default: 157 return "ERR" 158 } 159 }