github.com/gagliardetto/solana-go@v1.11.0/text/encoder.go (about) 1 // Copyright 2020 dfuse Platform Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package text 16 17 import ( 18 "fmt" 19 "io" 20 "reflect" 21 "strings" 22 23 "github.com/fatih/color" 24 ) 25 26 var ( 27 EncoderColorCyan = color.New(color.FgCyan) 28 EncoderColorYellow = color.New(color.FgYellow) 29 EncoderColorGreen = color.New(color.FgGreen) 30 EncoderColorWhite = color.New(color.FgWhite) 31 ) 32 33 type Encoder struct { 34 output io.Writer 35 indentLevel int 36 } 37 38 func NewEncoder(w io.Writer) *Encoder { 39 return &Encoder{ 40 output: w, 41 } 42 } 43 44 func isZero(rv reflect.Value) (b bool) { 45 return rv.Kind() == 0 46 } 47 48 func isNil(rv reflect.Value) (b bool) { 49 defer func(b bool) { 50 if err := recover(); err != nil { 51 b = true 52 } 53 }(b) 54 return rv.IsNil() 55 } 56 57 func (e *Encoder) Encode(v interface{}, option *Option) (err error) { 58 if option == nil { 59 option = &Option{} 60 } 61 return e.encode(reflect.ValueOf(v), option) 62 } 63 64 type Option struct { 65 indent bool 66 linear bool 67 fgColor *color.Color 68 NoTypeName bool 69 } 70 71 func (e *Encoder) encode(rv reflect.Value, option *Option) (err error) { 72 if option == nil { 73 option = &Option{} 74 } 75 76 if isZero(rv) { 77 return e.ToWriter("NIL_VALUE", option.indent, option.fgColor) 78 } 79 80 if enc, ok := rv.Interface().(TextEncodable); ok { 81 return enc.TextEncode(e, option) 82 } 83 84 //if sr, ok := rv.Interface().(fmt.Stringer); ok { 85 // return e.ToWriter(sr.String(), option.indent, option.fgColor) 86 //} 87 88 switch rv.Kind() { 89 case reflect.String: 90 return e.ToWriter(rv.String(), option.indent, option.fgColor) 91 case reflect.Uint8, reflect.Int8: 92 return e.ToWriter(fmt.Sprintf("%d", byte(rv.Uint())), option.indent, option.fgColor) 93 case reflect.Int16: 94 return e.ToWriter(fmt.Sprintf("%d", int16(rv.Int())), option.indent, option.fgColor) 95 case reflect.Uint16: 96 return e.ToWriter(fmt.Sprintf("%d", uint16(rv.Uint())), option.indent, option.fgColor) 97 case reflect.Int32: 98 return e.ToWriter(fmt.Sprintf("%d", int32(rv.Int())), option.indent, option.fgColor) 99 case reflect.Uint32: 100 return e.ToWriter(fmt.Sprintf("%d", uint32(rv.Uint())), option.indent, option.fgColor) 101 case reflect.Uint64: 102 return e.ToWriter(fmt.Sprintf("%d", rv.Uint()), option.indent, option.fgColor) 103 case reflect.Int64: 104 return e.ToWriter(fmt.Sprintf("%d", rv.Int()), option.indent, option.fgColor) 105 case reflect.Float32: 106 return e.ToWriter(fmt.Sprintf("%f", float32(rv.Float())), option.indent, option.fgColor) 107 case reflect.Float64: 108 return e.ToWriter(fmt.Sprintf("%f", rv.Float()), option.indent, option.fgColor) 109 case reflect.Bool: 110 return e.ToWriter(fmt.Sprintf("%t", rv.Bool()), option.indent, option.fgColor) 111 case reflect.Ptr: 112 return e.encode(rv.Elem(), option) 113 } 114 115 rv = reflect.Indirect(rv) 116 rt := rv.Type() 117 switch rt.Kind() { 118 case reflect.Array: 119 l := rt.Len() 120 e.indentLevel++ 121 for i := 0; i < l; i++ { 122 if err := e.ToWriter("\n", false, nil); err != nil { 123 return err 124 } 125 if err := e.ToWriter(fmt.Sprintf("[%d] ", i), true, nil); err != nil { 126 return err 127 } 128 if err = e.encode(rv.Index(i), option); err != nil { 129 return 130 } 131 } 132 e.indentLevel-- 133 case reflect.Slice: 134 l := rv.Len() 135 e.indentLevel++ 136 for i := 0; i < l; i++ { 137 138 if err := e.ToWriter("\n", false, nil); err != nil { 139 return err 140 } 141 142 if err := e.ToWriter(fmt.Sprintf("[%d] ", i), true, nil); err != nil { 143 return err 144 } 145 146 if err = e.encode(rv.Index(i), option); err != nil { 147 return 148 } 149 //if err := e.ToWriter("\n"); err != nil { 150 // return err 151 //} 152 } 153 e.indentLevel-- 154 case reflect.Struct: 155 if err = e.encodeStruct(rt, rv, option); err != nil { 156 return 157 } 158 159 case reflect.Map: 160 for _, mapKey := range rv.MapKeys() { 161 if err = e.Encode(mapKey.Interface(), option); err != nil { 162 return 163 } 164 165 if err = e.Encode(rv.MapIndex(mapKey).Interface(), option); err != nil { 166 return 167 } 168 } 169 170 default: 171 return e.ToWriter("NOT TEXTABLE", false, option.fgColor) 172 } 173 return 174 } 175 176 func (e *Encoder) ToWriter(s string, indent bool, c *color.Color) (err error) { 177 if indent { 178 indent := strings.Repeat(" ", e.indentLevel) 179 if _, err = e.output.Write([]byte(indent)); err != nil { 180 return err 181 } 182 } 183 if c != nil { 184 s = c.Sprintf("%s", s) 185 } 186 _, err = e.output.Write([]byte(s)) 187 188 return nil 189 } 190 191 func (e *Encoder) encodeStruct(rt reflect.Type, rv reflect.Value, option *Option) (err error) { 192 e.indentLevel++ 193 defer func() { 194 e.indentLevel-- 195 }() 196 197 if err := e.ToWriter("\n", false, nil); err != nil { 198 return err 199 } 200 201 if !option.NoTypeName { 202 if err := e.ToWriter(rt.Name(), true, EncoderColorCyan); err != nil { 203 return err 204 } 205 } 206 207 if !option.linear { 208 if err := e.ToWriter("\n", false, nil); err != nil { 209 return err 210 } 211 } else { 212 if err := e.ToWriter(" ", false, nil); err != nil { 213 return err 214 } 215 } 216 217 l := rv.NumField() 218 for i := 0; i < l; i++ { 219 structField := rt.Field(i) 220 fieldTag := parseFieldTag(structField.Tag) 221 fieldOption := &Option{ 222 fgColor: EncoderColorWhite, 223 linear: fieldTag.Linear, 224 NoTypeName: false, 225 } 226 227 fieldOption.linear = fieldTag.Linear 228 229 if fieldTag.Skip { 230 continue 231 } 232 233 rv := rv.Field(i) 234 235 if !rv.CanInterface() { 236 continue 237 } 238 239 if err := e.ToWriter(structField.Name+": ", !option.linear, EncoderColorGreen); err != nil { 240 return err 241 } 242 243 if err := e.encode(rv, fieldOption); err != nil { 244 return err 245 } 246 247 if !option.linear { 248 if err := e.ToWriter("\n", false, nil); err != nil { 249 return err 250 } 251 } else { 252 if err := e.ToWriter(" ", false, nil); err != nil { 253 return err 254 } 255 } 256 } 257 return nil 258 }