github.com/XiaoMi/Gaea@v1.2.5/parser/format/format.go (about) 1 // Copyright (c) 2014 The sortutil Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSES/STRUTIL-LICENSE file. 4 5 // Copyright 2015 PingCAP, Inc. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package format 19 20 import ( 21 "bytes" 22 "fmt" 23 "io" 24 "strings" 25 ) 26 27 const ( 28 st0 = iota 29 stBOL 30 stPERC 31 stBOLPERC 32 ) 33 34 // Formatter is an io.Writer extended formatter by a fmt.Printf like function Format. 35 type Formatter interface { 36 io.Writer 37 Format(format string, args ...interface{}) (n int, errno error) 38 } 39 40 type indentFormatter struct { 41 io.Writer 42 indent []byte 43 indentLevel int 44 state int 45 } 46 47 var replace = map[rune]string{ 48 '\000': "\\0", 49 '\'': "''", 50 '\n': "\\n", 51 '\r': "\\r", 52 } 53 54 // IndentFormatter returns a new Formatter which interprets %i and %u in the 55 // Format() formats string as indent and unindent commands. The commands can 56 // nest. The Formatter writes to io.Writer 'w' and inserts one 'indent' 57 // string per current indent level value. 58 // Behaviour of commands reaching negative indent levels is undefined. 59 // IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3) 60 // output: 61 // abc3%e 62 // x 63 // y 64 // z 65 // The Go quoted string literal form of the above is: 66 // "abc%%e\n\tx\n\tx\nz\n" 67 // The commands can be scattered between separate invocations of Format(), 68 // i.e. the formatter keeps track of the indent level and knows if it is 69 // positioned on start of a line and should emit indentation(s). 70 // The same output as above can be produced by e.g.: 71 // f := IndentFormatter(os.Stdout, " ") 72 // f.Format("abc%d%%e%i\nx\n", 3) 73 // f.Format("y\n%uz\n") 74 func IndentFormatter(w io.Writer, indent string) Formatter { 75 return &indentFormatter{w, []byte(indent), 0, stBOL} 76 } 77 78 func (f *indentFormatter) format(flat bool, format string, args ...interface{}) (n int, errno error) { 79 var buf = make([]byte, 0) 80 for i := 0; i < len(format); i++ { 81 c := format[i] 82 switch f.state { 83 case st0: 84 switch c { 85 case '\n': 86 cc := c 87 if flat && f.indentLevel != 0 { 88 cc = ' ' 89 } 90 buf = append(buf, cc) 91 f.state = stBOL 92 case '%': 93 f.state = stPERC 94 default: 95 buf = append(buf, c) 96 } 97 case stBOL: 98 switch c { 99 case '\n': 100 cc := c 101 if flat && f.indentLevel != 0 { 102 cc = ' ' 103 } 104 buf = append(buf, cc) 105 case '%': 106 f.state = stBOLPERC 107 default: 108 if !flat { 109 for i := 0; i < f.indentLevel; i++ { 110 buf = append(buf, f.indent...) 111 } 112 } 113 buf = append(buf, c) 114 f.state = st0 115 } 116 case stBOLPERC: 117 switch c { 118 case 'i': 119 f.indentLevel++ 120 f.state = stBOL 121 case 'u': 122 f.indentLevel-- 123 f.state = stBOL 124 default: 125 if !flat { 126 for i := 0; i < f.indentLevel; i++ { 127 buf = append(buf, f.indent...) 128 } 129 } 130 buf = append(buf, '%', c) 131 f.state = st0 132 } 133 case stPERC: 134 switch c { 135 case 'i': 136 f.indentLevel++ 137 f.state = st0 138 case 'u': 139 f.indentLevel-- 140 f.state = st0 141 default: 142 buf = append(buf, '%', c) 143 f.state = st0 144 } 145 default: 146 panic("unexpected state") 147 } 148 } 149 switch f.state { 150 case stPERC, stBOLPERC: 151 buf = append(buf, '%') 152 } 153 return f.Write([]byte(fmt.Sprintf(string(buf), args...))) 154 } 155 156 // Format implements Format interface. 157 func (f *indentFormatter) Format(format string, args ...interface{}) (n int, errno error) { 158 return f.format(false, format, args...) 159 } 160 161 type flatFormatter indentFormatter 162 163 // FlatFormatter returns a newly created Formatter with the same functionality as the one returned 164 // by IndentFormatter except it allows a newline in the 'format' string argument of Format 165 // to pass through if the indent level is current zero. 166 // 167 // If the indent level is non-zero then such new lines are changed to a space character. 168 // There is no indent string, the %i and %u format verbs are used solely to determine the indent level. 169 // 170 // The FlatFormatter is intended for flattening of normally nested structure textual representation to 171 // a one top level structure per line form. 172 // FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3) 173 // output in the form of a Go quoted string literal: 174 // "abc3%%e x y z\n" 175 func FlatFormatter(w io.Writer) Formatter { 176 return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter)) 177 } 178 179 // Format implements Format interface. 180 func (f *flatFormatter) Format(format string, args ...interface{}) (n int, errno error) { 181 return (*indentFormatter)(f).format(true, format, args...) 182 } 183 184 // OutputFormat output escape character with backslash. 185 func OutputFormat(s string) string { 186 var buf bytes.Buffer 187 for _, old := range s { 188 if newVal, ok := replace[old]; ok { 189 buf.WriteString(newVal) 190 continue 191 } 192 buf.WriteRune(old) 193 } 194 195 return buf.String() 196 } 197 198 // RestoreFlags mark the Restore format 199 type RestoreFlags uint64 200 201 // Mutually exclusive group of `RestoreFlags`: 202 // [RestoreStringSingleQuotes, RestoreStringDoubleQuotes] 203 // [RestoreKeyWordUppercase, RestoreKeyWordLowercase] 204 // [RestoreNameUppercase, RestoreNameLowercase] 205 // [RestoreNameDoubleQuotes, RestoreNameBackQuotes] 206 // The flag with the left position in each group has a higher priority. 207 const ( 208 RestoreStringSingleQuotes RestoreFlags = 1 << iota 209 RestoreStringDoubleQuotes 210 RestoreStringEscapeBackslash 211 212 RestoreKeyWordUppercase 213 RestoreKeyWordLowercase 214 215 RestoreNameUppercase 216 RestoreNameLowercase 217 RestoreNameDoubleQuotes 218 RestoreNameBackQuotes 219 ) 220 221 const ( 222 // DefaultRestoreFlags means default restore flags 223 DefaultRestoreFlags = RestoreStringSingleQuotes | RestoreKeyWordUppercase | RestoreNameBackQuotes 224 EscapeRestoreFlags = RestoreStringSingleQuotes | RestoreStringEscapeBackslash | RestoreKeyWordUppercase | RestoreNameBackQuotes 225 ) 226 227 func (rf RestoreFlags) has(flag RestoreFlags) bool { 228 return rf&flag != 0 229 } 230 231 // HasStringSingleQuotesFlag returns a boolean indicating when `rf` has `RestoreStringSingleQuotes` flag. 232 func (rf RestoreFlags) HasStringSingleQuotesFlag() bool { 233 return rf.has(RestoreStringSingleQuotes) 234 } 235 236 // HasStringDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreStringDoubleQuotes` flag. 237 func (rf RestoreFlags) HasStringDoubleQuotesFlag() bool { 238 return rf.has(RestoreStringDoubleQuotes) 239 } 240 241 // HasStringEscapeBackslashFlag returns a boolean indicating whether `rf` has `RestoreStringEscapeBackslash` flag. 242 func (rf RestoreFlags) HasStringEscapeBackslashFlag() bool { 243 return rf.has(RestoreStringEscapeBackslash) 244 } 245 246 // HasKeyWordUppercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordUppercase` flag. 247 func (rf RestoreFlags) HasKeyWordUppercaseFlag() bool { 248 return rf.has(RestoreKeyWordUppercase) 249 } 250 251 // HasKeyWordLowercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordLowercase` flag. 252 func (rf RestoreFlags) HasKeyWordLowercaseFlag() bool { 253 return rf.has(RestoreKeyWordLowercase) 254 } 255 256 // HasNameUppercaseFlag returns a boolean indicating whether `rf` has `RestoreNameUppercase` flag. 257 func (rf RestoreFlags) HasNameUppercaseFlag() bool { 258 return rf.has(RestoreNameUppercase) 259 } 260 261 // HasNameLowercaseFlag returns a boolean indicating whether `rf` has `RestoreNameLowercase` flag. 262 func (rf RestoreFlags) HasNameLowercaseFlag() bool { 263 return rf.has(RestoreNameLowercase) 264 } 265 266 // HasNameDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameDoubleQuotes` flag. 267 func (rf RestoreFlags) HasNameDoubleQuotesFlag() bool { 268 return rf.has(RestoreNameDoubleQuotes) 269 } 270 271 // HasNameBackQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameBackQuotes` flag. 272 func (rf RestoreFlags) HasNameBackQuotesFlag() bool { 273 return rf.has(RestoreNameBackQuotes) 274 } 275 276 // RestoreCtx is `Restore` context to hold flags and writer. 277 type RestoreCtx struct { 278 Flags RestoreFlags 279 In io.Writer 280 JoinLevel int 281 } 282 283 // NewRestoreCtx returns a new `RestoreCtx`. 284 func NewRestoreCtx(flags RestoreFlags, in io.Writer) *RestoreCtx { 285 return &RestoreCtx{flags, in, 0} 286 } 287 288 // WriteKeyWord writes the `keyWord` into writer. 289 // `keyWord` will be converted format(uppercase and lowercase for now) according to `RestoreFlags`. 290 func (ctx *RestoreCtx) WriteKeyWord(keyWord string) { 291 switch { 292 case ctx.Flags.HasKeyWordUppercaseFlag(): 293 keyWord = strings.ToUpper(keyWord) 294 case ctx.Flags.HasKeyWordLowercaseFlag(): 295 keyWord = strings.ToLower(keyWord) 296 } 297 fmt.Fprint(ctx.In, keyWord) 298 } 299 300 // WriteString writes the string into writer 301 // `str` may be wrapped in quotes and escaped according to RestoreFlags. 302 func (ctx *RestoreCtx) WriteString(str string) { 303 if ctx.Flags.HasStringEscapeBackslashFlag() { 304 str = strings.Replace(str, `\`, `\\`, -1) 305 } 306 quotes := "" 307 switch { 308 case ctx.Flags.HasStringSingleQuotesFlag(): 309 str = strings.Replace(str, `'`, `''`, -1) 310 quotes = `'` 311 case ctx.Flags.HasStringDoubleQuotesFlag(): 312 str = strings.Replace(str, `"`, `""`, -1) 313 quotes = `"` 314 } 315 fmt.Fprint(ctx.In, quotes, str, quotes) 316 } 317 318 // WriteName writes the name into writer 319 // `name` maybe wrapped in quotes and escaped according to RestoreFlags. 320 func (ctx *RestoreCtx) WriteName(name string) { 321 switch { 322 case ctx.Flags.HasNameUppercaseFlag(): 323 name = strings.ToUpper(name) 324 case ctx.Flags.HasNameLowercaseFlag(): 325 name = strings.ToLower(name) 326 } 327 quotes := "" 328 switch { 329 case ctx.Flags.HasNameDoubleQuotesFlag(): 330 name = strings.Replace(name, `"`, `""`, -1) 331 quotes = `"` 332 case ctx.Flags.HasNameBackQuotesFlag(): 333 name = strings.Replace(name, "`", "``", -1) 334 quotes = "`" 335 } 336 fmt.Fprint(ctx.In, quotes, name, quotes) 337 } 338 339 // WritePlain writes the plain text into writer without any handling. 340 func (ctx *RestoreCtx) WritePlain(plainText string) { 341 fmt.Fprint(ctx.In, plainText) 342 } 343 344 // WritePlainf write the plain text into writer without any handling. 345 func (ctx *RestoreCtx) WritePlainf(format string, a ...interface{}) { 346 fmt.Fprintf(ctx.In, format, a...) 347 }