1// Copyright (c) 2020, Peter Ohler, All rights reserved. 2 3package ojg 4 5import ( 6 "fmt" 7 "strconv" 8 "time" 9) 10 11const ( 12 // Normal is the Normal ANSI encoding sequence. 13 Normal = "\x1b[m" 14 // Black is the Black ANSI encoding sequence. 15 Black = "\x1b[30m" 16 // Red is the Red ANSI encoding sequence. 17 Red = "\x1b[31m" 18 // Green is the Green ANSI encoding sequence. 19 Green = "\x1b[32m" 20 // Yellow is the Yellow ANSI encoding sequence. 21 Yellow = "\x1b[33m" 22 // Blue is the Blue ANSI encoding sequence. 23 Blue = "\x1b[34m" 24 // Magenta is the Magenta ANSI encoding sequence. 25 Magenta = "\x1b[35m" 26 // Cyan is the Cyan ANSI encoding sequence. 27 Cyan = "\x1b[36m" 28 // White is the White ANSI encoding sequence. 29 White = "\x1b[37m" 30 // Gray is the Gray ANSI encoding sequence. 31 Gray = "\x1b[90m" 32 // BrightRed is the BrightRed ANSI encoding sequence. 33 BrightRed = "\x1b[91m" 34 // BrightGreen is the BrightGreen ANSI encoding sequence. 35 BrightGreen = "\x1b[92m" 36 // BrightYellow is the BrightYellow ANSI encoding sequence. 37 BrightYellow = "\x1b[93m" 38 // BrightBlue is the BrightBlue ANSI encoding sequence. 39 BrightBlue = "\x1b[94m" 40 // BrightMagenta is the BrightMagenta ANSI encoding sequence. 41 BrightMagenta = "\x1b[95m" 42 // BrightCyan is the BrightCyan ANSI encoding sequence. 43 BrightCyan = "\x1b[96m" 44 // BrightWhite is the BrightWhite ANSI encoding sequence. 45 BrightWhite = "\x1b[97m" 46 47 // BytesAsString indicates []byte should be encoded as a string. 48 BytesAsString = iota 49 // BytesAsBase64 indicates []byte should be encoded as base64. 50 BytesAsBase64 51 // BytesAsArray indicates []byte should be encoded as an array if integers. 52 BytesAsArray 53 54 // MaskByTag is the mask for byTag fields. 55 MaskByTag = byte(0x10) 56 // MaskExact is the mask for Exact fields. 57 MaskExact = byte(0x08) // exact key vs lowwer case first letter 58 // MaskPretty is the mask for Pretty fields. 59 MaskPretty = byte(0x04) 60 // MaskNested is the mask for Nested fields. 61 MaskNested = byte(0x02) 62 // MaskSen is the mask for Sen fields. 63 MaskSen = byte(0x01) 64 // MaskSet is the mask for Set fields. 65 MaskSet = byte(0x20) 66 // MaskIndex is the mask for an index that has been set up. 67 MaskIndex = byte(0x1f) 68) 69 70var ( 71 // DefaultOptions default options that can be set as desired. 72 DefaultOptions = Options{ 73 InitSize: 256, 74 SyntaxColor: Normal, 75 KeyColor: Blue, 76 NullColor: Red, 77 BoolColor: Yellow, 78 NumberColor: Cyan, 79 StringColor: Green, 80 TimeColor: Magenta, 81 HTMLUnsafe: true, 82 WriteLimit: 1024, 83 } 84 85 // BrightOptions encoding options for color encoding. 86 BrightOptions = Options{ 87 InitSize: 256, 88 SyntaxColor: Normal, 89 KeyColor: BrightBlue, 90 NullColor: BrightRed, 91 BoolColor: BrightYellow, 92 NumberColor: BrightCyan, 93 StringColor: BrightGreen, 94 TimeColor: BrightMagenta, 95 WriteLimit: 1024, 96 } 97 98 // GoOptions are the options closest to the go json package. 99 GoOptions = Options{ 100 InitSize: 256, 101 SyntaxColor: Normal, 102 KeyColor: Blue, 103 NullColor: Red, 104 BoolColor: Yellow, 105 NumberColor: Cyan, 106 StringColor: Green, 107 TimeColor: Magenta, 108 CreateKey: "", 109 FullTypePath: false, 110 OmitNil: false, 111 UseTags: true, 112 KeyExact: true, 113 NestEmbed: false, 114 BytesAs: BytesAsBase64, 115 WriteLimit: 1024, 116 } 117 118 // HTMLOptions defines color options for generating colored HTML. The 119 // encoding is suitable for use in a <pre> element. 120 HTMLOptions = Options{ 121 InitSize: 256, 122 SyntaxColor: "<span>", 123 KeyColor: `<span style="color:#44f">`, 124 NullColor: `<span style="color:red">`, 125 BoolColor: `<span style="color:#a40">`, 126 NumberColor: `<span style="color:#04a">`, 127 StringColor: `<span style="color:green">`, 128 TimeColor: `<span style="color:#f0f">`, 129 NoColor: "</span>", 130 HTMLUnsafe: false, 131 WriteLimit: 1024, 132 } 133) 134 135// Options for writing data to JSON. 136type Options struct { 137 138 // Indent for the output. 139 Indent int 140 141 // Tab if true will indent using tabs and ignore the Indent member. 142 Tab bool 143 144 // Sort object members if true. 145 Sort bool 146 147 // OmitNil skips the writing of nil values in an object. 148 OmitNil bool 149 150 // InitSize is the initial buffer size. 151 InitSize int 152 153 // WriteLimit is the size of the buffer that will trigger a write when 154 // using a writer. 155 WriteLimit int 156 157 // TimeFormat defines how time is encoded. Options are to use a 158 // time. layout string format such as time.RFC3339Nano, "second" for a 159 // decimal representation, "nano" for a an integer. For decompose setting 160 // to "time" will leave it unchanged. 161 TimeFormat string 162 163 // TimeWrap if not empty encoded time as an object with a single member. For 164 // example if set to "@" then and TimeFormat is RFC3339Nano then the encoded 165 // time will look like '{"@":"2020-04-12T16:34:04.123456789Z"}' 166 TimeWrap string 167 168 // TimeMap if true will encode time as a map with a create key and a 169 // 'value' member formatted according to the TimeFormat options. 170 TimeMap bool 171 172 // CreateKey if set is the key to use when encoding objects that can later 173 // be reconstituted with an Unmarshall call. This is only use when writing 174 // simple types where one of the object in an array or map is not a 175 // Simplifier. Reflection is used to encode all public members of the 176 // object if possible. For example, is CreateKey is set to "type" this 177 // might be the encoding. 178 // 179 // { "type": "MyType", "a": 3, "b": true } 180 // 181 CreateKey string 182 183 // NoReflect if true does not use reflection to encode an object. This is 184 // only considered if the CreateKey is empty. 185 NoReflect bool 186 187 // FullTypePath if true includes the full type name and path when used 188 // with the CreateKey. 189 FullTypePath bool 190 191 // Color if true will colorize the output. 192 Color bool 193 194 // SyntaxColor is the color for syntax in the JSON output. 195 SyntaxColor string 196 197 // KeyColor is the color for a key in the JSON output. 198 KeyColor string 199 200 // NullColor is the color for a null in the JSON output. 201 NullColor string 202 203 // BoolColor is the color for a bool in the JSON output. 204 BoolColor string 205 206 // NumberColor is the color for a number in the JSON output. 207 NumberColor string 208 209 // StringColor is the color for a string in the JSON output. 210 StringColor string 211 212 // TimeColor is the color for a time.Time in the JSON output. 213 TimeColor string 214 215 // NoColor turns the color off. 216 NoColor string 217 218 // UseTags if true will use the json annotation tags when marhsalling, 219 // writing, or decomposing an struct. If no tag is present then the 220 // KeyExact flag is referenced to determine the key. 221 UseTags bool 222 223 // KeyExact if true will use the exact field name for an encoded struct 224 // field. If false the key style most often seen in JSON files where the 225 // first character of the object keys is lowercase. 226 KeyExact bool 227 228 // HTMLUnsafe if true turns off escaping of &, <, and >. 229 HTMLUnsafe bool 230 231 // NestEmbed if true will generate an element for each anonymous embedded 232 // field. 233 NestEmbed bool 234 235 // BytesAs indicates how []byte fields should be encoded. Choices are 236 // BytesAsString, BytesAsBase64 (the go json package default), or 237 // BytesAsArray. 238 BytesAs int 239 240 // Converter to use when decomposing or altering if non nil. 241 Converter *Converter 242} 243 244// AppendTime appends a time string to the buffer. 245func (o *Options) AppendTime(buf []byte, t time.Time, sen bool) []byte { 246 if o.TimeMap { 247 buf = append(buf, '{') 248 if sen { 249 buf = AppendSENString(buf, o.CreateKey, o.HTMLUnsafe) 250 } else { 251 buf = AppendJSONString(buf, o.CreateKey, o.HTMLUnsafe) 252 } 253 buf = append(buf, ':') 254 if sen { 255 if o.FullTypePath { 256 buf = append(buf, `"time/Time" value:`...) 257 } else { 258 buf = append(buf, "Time value:"...) 259 } 260 } else { 261 if o.FullTypePath { 262 buf = append(buf, `"time/Time","value":`...) 263 } else { 264 buf = append(buf, `"Time","value":`...) 265 } 266 } 267 } else if 0 < len(o.TimeWrap) { 268 buf = append(buf, '{') 269 if sen { 270 buf = AppendSENString(buf, o.TimeWrap, o.HTMLUnsafe) 271 } else { 272 buf = AppendJSONString(buf, o.TimeWrap, o.HTMLUnsafe) 273 } 274 buf = append(buf, ':') 275 } 276 switch o.TimeFormat { 277 case "", "nano": 278 buf = strconv.AppendInt(buf, t.UnixNano(), 10) 279 case "second": 280 // Decimal format but float is not accurate enough so build the output 281 // in two parts. 282 nano := t.UnixNano() 283 secs := nano / int64(time.Second) 284 if 0 < nano { 285 buf = append(buf, fmt.Sprintf("%d.%09d", secs, nano-(secs*int64(time.Second)))...) 286 } else { 287 buf = append(buf, fmt.Sprintf("%d.%09d", secs, -(nano-(secs*int64(time.Second))))...) 288 } 289 default: 290 buf = append(buf, '"') 291 buf = t.AppendFormat(buf, o.TimeFormat) 292 buf = append(buf, '"') 293 } 294 if 0 < len(o.TimeWrap) || o.TimeMap { 295 buf = append(buf, '}') 296 } 297 return buf 298} 299 300// DecomposeTime encodes time in the format specified by the settings of the 301// options. 302func (o *Options) DecomposeTime(t time.Time) (v interface{}) { 303 switch o.TimeFormat { 304 case "time": 305 v = t 306 case "", "nano": 307 v = t.UnixNano() 308 case "second": 309 v = float64(t.UnixNano()) / float64(time.Second) 310 default: 311 v = t.Format(o.TimeFormat) 312 } 313 if o.TimeMap { 314 if o.FullTypePath { 315 v = map[string]interface{}{o.CreateKey: "time/Time", "value": v} 316 } else { 317 v = map[string]interface{}{o.CreateKey: "Time", "value": v} 318 } 319 } else if 0 < len(o.TimeWrap) { 320 v = map[string]interface{}{o.TimeWrap: v} 321 } 322 return 323} 324