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  }