github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/sqlparse/tidbparser/dependency/util/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  )
    25  
    26  const (
    27  	st0 = iota
    28  	stBOL
    29  	stPERC
    30  	stBOLPERC
    31  )
    32  
    33  // Formatter is an io.Writer extended formatter by a fmt.Printf like function Format.
    34  type Formatter interface {
    35  	io.Writer
    36  	Format(format string, args ...interface{}) (n int, errno error)
    37  }
    38  
    39  type indentFormatter struct {
    40  	io.Writer
    41  	indent      []byte
    42  	indentLevel int
    43  	state       int
    44  }
    45  
    46  var replace = map[rune]string{
    47  	'\000': "\\0",
    48  	'\'':   "''",
    49  	'\n':   "\\n",
    50  	'\r':   "\\r",
    51  }
    52  
    53  // IndentFormatter returns a new Formatter which interprets %i and %u in the
    54  // Format() formats string as indent and unindent commands. The commands can
    55  // nest. The Formatter writes to io.Writer 'w' and inserts one 'indent'
    56  // string per current indent level value.
    57  // Behaviour of commands reaching negative indent levels is undefined.
    58  //
    59  //	IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
    60  //
    61  // output:
    62  //
    63  //	abc3%e
    64  //	    x
    65  //	    y
    66  //	z
    67  //
    68  // The Go quoted string literal form of the above is:
    69  //
    70  //	"abc%%e\n\tx\n\tx\nz\n"
    71  //
    72  // The commands can be scattered between separate invocations of Format(),
    73  // i.e. the formatter keeps track of the indent level and knows if it is
    74  // positioned on start of a line and should emit indentation(s).
    75  // The same output as above can be produced by e.g.:
    76  //
    77  //	f := IndentFormatter(os.Stdout, " ")
    78  //	f.Format("abc%d%%e%i\nx\n", 3)
    79  //	f.Format("y\n%uz\n")
    80  func IndentFormatter(w io.Writer, indent string) Formatter {
    81  	return &indentFormatter{w, []byte(indent), 0, stBOL}
    82  }
    83  
    84  func (f *indentFormatter) format(flat bool, format string, args ...interface{}) (n int, errno error) {
    85  	buf := make([]byte, 0)
    86  	for i := 0; i < len(format); i++ {
    87  		c := format[i]
    88  		switch f.state {
    89  		case st0:
    90  			switch c {
    91  			case '\n':
    92  				cc := c
    93  				if flat && f.indentLevel != 0 {
    94  					cc = ' '
    95  				}
    96  				buf = append(buf, cc)
    97  				f.state = stBOL
    98  			case '%':
    99  				f.state = stPERC
   100  			default:
   101  				buf = append(buf, c)
   102  			}
   103  		case stBOL:
   104  			switch c {
   105  			case '\n':
   106  				cc := c
   107  				if flat && f.indentLevel != 0 {
   108  					cc = ' '
   109  				}
   110  				buf = append(buf, cc)
   111  			case '%':
   112  				f.state = stBOLPERC
   113  			default:
   114  				if !flat {
   115  					for i := 0; i < f.indentLevel; i++ {
   116  						buf = append(buf, f.indent...)
   117  					}
   118  				}
   119  				buf = append(buf, c)
   120  				f.state = st0
   121  			}
   122  		case stBOLPERC:
   123  			switch c {
   124  			case 'i':
   125  				f.indentLevel++
   126  				f.state = stBOL
   127  			case 'u':
   128  				f.indentLevel--
   129  				f.state = stBOL
   130  			default:
   131  				if !flat {
   132  					for i := 0; i < f.indentLevel; i++ {
   133  						buf = append(buf, f.indent...)
   134  					}
   135  				}
   136  				buf = append(buf, '%', c)
   137  				f.state = st0
   138  			}
   139  		case stPERC:
   140  			switch c {
   141  			case 'i':
   142  				f.indentLevel++
   143  				f.state = st0
   144  			case 'u':
   145  				f.indentLevel--
   146  				f.state = st0
   147  			default:
   148  				buf = append(buf, '%', c)
   149  				f.state = st0
   150  			}
   151  		default:
   152  			panic("unexpected state")
   153  		}
   154  	}
   155  	switch f.state {
   156  	case stPERC, stBOLPERC:
   157  		buf = append(buf, '%')
   158  	}
   159  	return f.Write([]byte(fmt.Sprintf(string(buf), args...)))
   160  }
   161  
   162  // Format implements Format interface.
   163  func (f *indentFormatter) Format(format string, args ...interface{}) (n int, errno error) {
   164  	return f.format(false, format, args...)
   165  }
   166  
   167  type flatFormatter indentFormatter
   168  
   169  // FlatFormatter returns a newly created Formatter with the same functionality as the one returned
   170  // by IndentFormatter except it allows a newline in the 'format' string argument of Format
   171  // to pass through if the indent level is current zero.
   172  //
   173  // If the indent level is non-zero then such new lines are changed to a space character.
   174  // There is no indent string, the %i and %u format verbs are used solely to determine the indent level.
   175  //
   176  // The FlatFormatter is intended for flattening of normally nested structure textual representation to
   177  // a one top level structure per line form.
   178  //
   179  //	FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
   180  //
   181  // output in the form of a Go quoted string literal:
   182  //
   183  //	"abc3%%e x y z\n"
   184  func FlatFormatter(w io.Writer) Formatter {
   185  	return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter))
   186  }
   187  
   188  // Format implements Format interface.
   189  func (f *flatFormatter) Format(format string, args ...interface{}) (n int, errno error) {
   190  	return (*indentFormatter)(f).format(true, format, args...)
   191  }
   192  
   193  // OutputFormat output escape character with backslash.
   194  func OutputFormat(s string) string {
   195  	var buf bytes.Buffer
   196  	for _, old := range s {
   197  		if newVal, ok := replace[old]; ok {
   198  			buf.WriteString(newVal)
   199  			continue
   200  		}
   201  		buf.WriteRune(old)
   202  	}
   203  
   204  	return buf.String()
   205  }