github.com/mithrandie/csvq@v1.18.1/lib/option/utils.go (about)

     1  package option
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  	"unicode"
    11  
    12  	"github.com/mithrandie/go-text"
    13  	txjson "github.com/mithrandie/go-text/json"
    14  )
    15  
    16  func EscapeString(s string) string {
    17  	runes := []rune(s)
    18  	var buf bytes.Buffer
    19  
    20  	for _, r := range runes {
    21  		switch r {
    22  		case '\a':
    23  			buf.WriteString("\\a")
    24  		case '\b':
    25  			buf.WriteString("\\b")
    26  		case '\f':
    27  			buf.WriteString("\\f")
    28  		case '\n':
    29  			buf.WriteString("\\n")
    30  		case '\r':
    31  			buf.WriteString("\\r")
    32  		case '\t':
    33  			buf.WriteString("\\t")
    34  		case '\v':
    35  			buf.WriteString("\\v")
    36  		case '\'':
    37  			buf.WriteString("\\'")
    38  		case '\\':
    39  			buf.WriteString("\\\\")
    40  		default:
    41  			buf.WriteRune(r)
    42  		}
    43  	}
    44  	return buf.String()
    45  }
    46  
    47  func UnescapeString(s string, quote rune) string {
    48  	runes := []rune(s)
    49  	var buf bytes.Buffer
    50  
    51  	escaped := false
    52  	quoteRune := rune(0)
    53  	for _, r := range runes {
    54  		if 0 < quoteRune {
    55  			buf.WriteRune(quoteRune)
    56  			if r == quoteRune {
    57  				quoteRune = 0
    58  				continue
    59  			}
    60  			quoteRune = 0
    61  		}
    62  
    63  		if escaped {
    64  			switch r {
    65  			case 'a':
    66  				buf.WriteRune('\a')
    67  			case 'b':
    68  				buf.WriteRune('\b')
    69  			case 'f':
    70  				buf.WriteRune('\f')
    71  			case 'n':
    72  				buf.WriteRune('\n')
    73  			case 'r':
    74  				buf.WriteRune('\r')
    75  			case 't':
    76  				buf.WriteRune('\t')
    77  			case 'v':
    78  				buf.WriteRune('\v')
    79  			case '"', '\'', '\\':
    80  				buf.WriteRune(r)
    81  			default:
    82  				buf.WriteRune('\\')
    83  				buf.WriteRune(r)
    84  			}
    85  			escaped = false
    86  			continue
    87  		}
    88  
    89  		switch r {
    90  		case '\\':
    91  			escaped = true
    92  		case quote:
    93  			quoteRune = r
    94  		default:
    95  			buf.WriteRune(r)
    96  		}
    97  	}
    98  	if escaped {
    99  		buf.WriteRune('\\')
   100  	}
   101  
   102  	return buf.String()
   103  }
   104  
   105  func EscapeIdentifier(s string) string {
   106  	runes := []rune(s)
   107  	var buf bytes.Buffer
   108  
   109  	for _, r := range runes {
   110  		switch r {
   111  		case '\a':
   112  			buf.WriteString("\\a")
   113  		case '\b':
   114  			buf.WriteString("\\b")
   115  		case '\f':
   116  			buf.WriteString("\\f")
   117  		case '\n':
   118  			buf.WriteString("\\n")
   119  		case '\r':
   120  			buf.WriteString("\\r")
   121  		case '\t':
   122  			buf.WriteString("\\t")
   123  		case '\v':
   124  			buf.WriteString("\\v")
   125  		case '`':
   126  			buf.WriteString("\\`")
   127  		case '\\':
   128  			buf.WriteString("\\\\")
   129  		default:
   130  			buf.WriteRune(r)
   131  		}
   132  	}
   133  	return buf.String()
   134  }
   135  
   136  func UnescapeIdentifier(s string, quote rune) string {
   137  	runes := []rune(s)
   138  	var buf bytes.Buffer
   139  
   140  	escaped := false
   141  	quoteRune := rune(0)
   142  	for _, r := range runes {
   143  		if 0 < quoteRune {
   144  			buf.WriteRune(quoteRune)
   145  			if r == quoteRune {
   146  				quoteRune = 0
   147  				continue
   148  			}
   149  			quoteRune = 0
   150  		}
   151  
   152  		if escaped {
   153  			switch r {
   154  			case 'a':
   155  				buf.WriteRune('\a')
   156  			case 'b':
   157  				buf.WriteRune('\b')
   158  			case 'f':
   159  				buf.WriteRune('\f')
   160  			case 'n':
   161  				buf.WriteRune('\n')
   162  			case 'r':
   163  				buf.WriteRune('\r')
   164  			case 't':
   165  				buf.WriteRune('\t')
   166  			case 'v':
   167  				buf.WriteRune('\v')
   168  			case '"', '`', '\\':
   169  				buf.WriteRune(r)
   170  			default:
   171  				buf.WriteRune('\\')
   172  				buf.WriteRune(r)
   173  			}
   174  			escaped = false
   175  			continue
   176  		}
   177  
   178  		switch r {
   179  		case '\\':
   180  			escaped = true
   181  		case quote:
   182  			quoteRune = r
   183  		default:
   184  			buf.WriteRune(r)
   185  		}
   186  	}
   187  	if escaped {
   188  		buf.WriteRune('\\')
   189  	}
   190  
   191  	return buf.String()
   192  }
   193  
   194  func QuoteString(s string) string {
   195  	return "'" + EscapeString(s) + "'"
   196  }
   197  
   198  func QuoteIdentifier(s string) string {
   199  	return "`" + EscapeIdentifier(s) + "`"
   200  }
   201  
   202  func VariableSymbol(s string) string {
   203  	return VariableSign + s
   204  }
   205  
   206  func FlagSymbol(s string) string {
   207  	return FlagSign + s
   208  }
   209  
   210  func EnvironmentVariableSymbol(s string) string {
   211  	if MustBeEnclosed(s) {
   212  		s = QuoteIdentifier(s)
   213  	}
   214  	return EnvironmentVariableSign + s
   215  }
   216  
   217  func EnclosedEnvironmentVariableSymbol(s string) string {
   218  	return EnvironmentVariableSign + QuoteIdentifier(s)
   219  }
   220  
   221  func MustBeEnclosed(s string) bool {
   222  	if len(s) == 0 {
   223  		return false
   224  	}
   225  
   226  	runes := []rune(s)
   227  
   228  	if runes[0] != '_' && !unicode.IsLetter(runes[0]) {
   229  		return true
   230  	}
   231  
   232  	for i := 1; i < len(runes); i++ {
   233  		if s[i] != '_' && !unicode.IsLetter(runes[i]) && !unicode.IsDigit(runes[i]) {
   234  			return true
   235  		}
   236  	}
   237  	return false
   238  }
   239  
   240  func RuntimeInformationSymbol(s string) string {
   241  	return RuntimeInformationSign + s
   242  }
   243  
   244  func FormatInt(i int, thousandsSeparator string) string {
   245  	return FormatNumber(float64(i), 0, ".", thousandsSeparator, "")
   246  }
   247  
   248  func FormatNumber(f float64, precision int, decimalPoint string, thousandsSeparator string, decimalSeparator string) string {
   249  	s := strconv.FormatFloat(f, 'f', precision, 64)
   250  
   251  	parts := strings.Split(s, ".")
   252  	intPart := parts[0]
   253  	decPart := ""
   254  	if 1 < len(parts) {
   255  		decPart = parts[1]
   256  	}
   257  
   258  	intPlaces := make([]string, 0, (len(intPart)/3)+1)
   259  	intLen := len(intPart)
   260  	for i := intLen / 3; i >= 0; i-- {
   261  		end := intLen - i*3
   262  		if end == 0 {
   263  			continue
   264  		}
   265  
   266  		start := intLen - (i+1)*3
   267  		if start < 0 {
   268  			start = 0
   269  		}
   270  		intPlaces = append(intPlaces, intPart[start:end])
   271  	}
   272  
   273  	decPlaces := make([]string, 0, (len(decPart)/3)+1)
   274  	for i := 0; i < len(decPart); i = i + 3 {
   275  		end := i + 3
   276  		if len(decPart) < end {
   277  			end = len(decPart)
   278  		}
   279  		decPlaces = append(decPlaces, decPart[i:end])
   280  	}
   281  
   282  	formatted := strings.Join(intPlaces, thousandsSeparator)
   283  	if 0 < len(decPlaces) {
   284  		formatted = formatted + decimalPoint + strings.Join(decPlaces, decimalSeparator)
   285  	}
   286  
   287  	return formatted
   288  }
   289  
   290  func ParseEncoding(s string) (text.Encoding, error) {
   291  	encoding, err := text.ParseEncoding(s)
   292  	if err != nil {
   293  		err = errors.New("encoding must be one of AUTO|UTF8|UTF8M|UTF16|UTF16BE|UTF16LE|UTF16BEM|UTF16LEM|SJIS")
   294  	}
   295  	return encoding, err
   296  }
   297  
   298  func ParseLineBreak(s string) (text.LineBreak, error) {
   299  	lb, err := text.ParseLineBreak(s)
   300  	if err != nil {
   301  		err = errors.New("line-break must be one of CRLF|CR|LF")
   302  	}
   303  	return lb, err
   304  }
   305  func ParseDelimiter(s string) (rune, error) {
   306  	r := []rune(UnescapeString(s, '\''))
   307  	if len(r) != 1 {
   308  		return 0, errors.New("delimiter must be one character")
   309  	}
   310  	return r[0], nil
   311  }
   312  
   313  func ParseDelimiterPositions(s string) ([]int, bool, error) {
   314  	s = UnescapeString(s, '\'')
   315  	var delimiterPositions []int = nil
   316  	singleLine := false
   317  
   318  	if !strings.EqualFold(DelimitAutomatically, s) {
   319  		if strings.HasPrefix(s, "s[") || strings.HasPrefix(s, "S[") {
   320  			singleLine = true
   321  			s = s[1:]
   322  		}
   323  		err := json.Unmarshal([]byte(s), &delimiterPositions)
   324  		if err != nil {
   325  			return delimiterPositions, singleLine, errors.New(fmt.Sprintf("delimiter positions must be %q or a JSON array of integers", DelimitAutomatically))
   326  		}
   327  	}
   328  	return delimiterPositions, singleLine, nil
   329  }
   330  
   331  func ParseFormat(s string, et txjson.EscapeType) (Format, txjson.EscapeType, error) {
   332  	var fm Format
   333  	switch strings.ToUpper(s) {
   334  	case "CSV":
   335  		fm = CSV
   336  	case "TSV":
   337  		fm = TSV
   338  	case "FIXED":
   339  		fm = FIXED
   340  	case "JSON":
   341  		fm = JSON
   342  	case "JSONL":
   343  		fm = JSONL
   344  	case "LTSV":
   345  		fm = LTSV
   346  	case "GFM":
   347  		fm = GFM
   348  	case "ORG":
   349  		fm = ORG
   350  	case "BOX":
   351  		fm = BOX
   352  	case "TEXT":
   353  		fm = TEXT
   354  	case "JSONH":
   355  		fm = JSON
   356  		et = txjson.HexDigits
   357  	case "JSONA":
   358  		fm = JSON
   359  		et = txjson.AllWithHexDigits
   360  	default:
   361  		return fm, et, errors.New("format must be one of CSV|TSV|FIXED|JSON|JSONL|LTSV|GFM|ORG|BOX|TEXT")
   362  	}
   363  	return fm, et, nil
   364  }
   365  
   366  func ParseJsonEscapeType(s string) (txjson.EscapeType, error) {
   367  	var escape txjson.EscapeType
   368  	switch strings.ToUpper(s) {
   369  	case "BACKSLASH":
   370  		escape = txjson.Backslash
   371  	case "HEX":
   372  		escape = txjson.HexDigits
   373  	case "HEXALL":
   374  		escape = txjson.AllWithHexDigits
   375  	default:
   376  		return escape, errors.New("json escape type must be one of BACKSLASH|HEX|HEXALL")
   377  	}
   378  	return escape, nil
   379  }
   380  
   381  func AppendStrIfNotExist(list []string, elem string) []string {
   382  	if len(elem) < 1 {
   383  		return list
   384  	}
   385  	for _, v := range list {
   386  		if elem == v {
   387  			return list
   388  		}
   389  	}
   390  	return append(list, elem)
   391  }
   392  
   393  func TextWidth(s string, flags *Flags) int {
   394  	return text.Width(s, flags.ExportOptions.EastAsianEncoding, flags.ExportOptions.CountDiacriticalSign, flags.ExportOptions.CountFormatCode)
   395  }
   396  
   397  func RuneWidth(r rune, flags *Flags) int {
   398  	return text.RuneWidth(r, flags.ExportOptions.EastAsianEncoding, flags.ExportOptions.CountDiacriticalSign, flags.ExportOptions.CountFormatCode)
   399  }
   400  
   401  func TrimSpace(s string) string {
   402  	if 0 < len(s) && (unicode.IsSpace(rune(s[0])) || unicode.IsSpace(rune(s[len(s)-1]))) {
   403  		s = strings.TrimSpace(s)
   404  	}
   405  	return s
   406  }