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  }