a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
1package log 2 3import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "time" 8) 9 10func (l *Logger) jsonFormatter(keyvals ...interface{}) { 11 jw := &jsonWriter{w: &l.b} 12 jw.start() 13 14 i := 0 15 for i < len(keyvals) { 16 switch kv := keyvals[i].(type) { 17 case slogAttr: 18 l.jsonFormatterRoot(jw, kv.Key, kv.Value) 19 i++ 20 default: 21 if i+1 < len(keyvals) { 22 l.jsonFormatterRoot(jw, keyvals[i], keyvals[i+1]) 23 } 24 i += 2 25 } 26 } 27 28 jw.end() 29 l.b.WriteRune('\n') 30} 31 32func (l *Logger) jsonFormatterRoot(jw *jsonWriter, key, value any) { 33 switch key { 34 case TimestampKey: 35 if t, ok := value.(time.Time); ok { 36 jw.objectItem(TimestampKey, t.Format(l.timeFormat)) 37 } 38 case LevelKey: 39 if level, ok := value.(Level); ok { 40 jw.objectItem(LevelKey, level.String()) 41 } 42 case CallerKey: 43 if caller, ok := value.(string); ok { 44 jw.objectItem(CallerKey, caller) 45 } 46 case PrefixKey: 47 if prefix, ok := value.(string); ok { 48 jw.objectItem(PrefixKey, prefix) 49 } 50 case MessageKey: 51 if msg := value; msg != nil { 52 jw.objectItem(MessageKey, fmt.Sprint(msg)) 53 } 54 default: 55 l.jsonFormatterItem(jw, key, value) 56 } 57} 58 59func (l *Logger) jsonFormatterItem(jw *jsonWriter, key, value any) { 60 switch k := key.(type) { 61 case fmt.Stringer: 62 jw.objectKey(k.String()) 63 case error: 64 jw.objectKey(k.Error()) 65 default: 66 jw.objectKey(fmt.Sprint(k)) 67 } 68 switch v := value.(type) { 69 case error: 70 jw.objectValue(v.Error()) 71 case slogLogValuer: 72 l.writeSlogValue(jw, v.LogValue()) 73 case slogValue: 74 l.writeSlogValue(jw, v.Resolve()) 75 case fmt.Stringer: 76 jw.objectValue(v.String()) 77 default: 78 jw.objectValue(v) 79 } 80} 81 82func (l *Logger) writeSlogValue(jw *jsonWriter, v slogValue) { 83 switch v.Kind() { //nolint:exhaustive 84 case slogKindGroup: 85 jw.start() 86 for _, attr := range v.Group() { 87 l.jsonFormatterItem(jw, attr.Key, attr.Value) 88 } 89 jw.end() 90 default: 91 jw.objectValue(v.Any()) 92 } 93} 94 95type jsonWriter struct { 96 w *bytes.Buffer 97 d int 98} 99 100func (w *jsonWriter) start() { 101 w.w.WriteRune('{') 102 w.d = 0 103} 104 105func (w *jsonWriter) end() { 106 w.w.WriteRune('}') 107} 108 109func (w *jsonWriter) objectItem(key string, value any) { 110 w.objectKey(key) 111 w.objectValue(value) 112} 113 114func (w *jsonWriter) objectKey(key string) { 115 if w.d > 0 { 116 w.w.WriteRune(',') 117 } 118 w.d++ 119 120 pos := w.w.Len() 121 err := w.writeEncoded(key) 122 if err != nil { 123 w.w.Truncate(pos) 124 w.w.WriteString(`"invalid key"`) 125 } 126 w.w.WriteRune(':') 127} 128 129func (w *jsonWriter) objectValue(value any) { 130 pos := w.w.Len() 131 err := w.writeEncoded(value) 132 if err != nil { 133 w.w.Truncate(pos) 134 w.w.WriteString(`"invalid value"`) 135 } 136} 137 138func (w *jsonWriter) writeEncoded(v any) error { 139 e := json.NewEncoder(w.w) 140 e.SetEscapeHTML(false) 141 if err := e.Encode(v); err != nil { 142 return fmt.Errorf("failed to encode value: %w", err) 143 } 144 145 // trailing \n added by json.Encode 146 b := w.w.Bytes() 147 if len(b) > 0 && b[len(b)-1] == '\n' { 148 w.w.Truncate(w.w.Len() - 1) 149 } 150 return nil 151}