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