summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-kv/util.go
blob: c0c8ccdab00cd82a0f9d6a6019475cb56196ab78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package kv

import (
	"strconv"
	"strings"

	"codeberg.org/gruf/go-byteutil"
	"codeberg.org/gruf/go-kv/format"
)

// AppendQuoteString will append (and escape/quote where necessary) a field string.
func AppendQuoteString(buf *byteutil.Buffer, str string) {
	switch {
	case len(str) == 0:
		// Append empty quotes.
		buf.B = append(buf.B, `""`...)
		return

	case len(str) == 1:
		// Append quote single byte.
		buf.B = append(buf.B, '\'')
		buf.B = append(buf.B, format.Byte2Str(str[0])...)
		buf.B = append(buf.B, '\'')
		return

	case len(str) > format.SingleTermLine || !format.IsSafeASCII(str):
		// Long line or contains non-ascii chars.
		buf.B = strconv.AppendQuote(buf.B, str)
		return

	case !isQuoted(str):
		// Not single/double quoted already.

		if format.ContainsSpaceOrTab(str) {
			// Quote un-enclosed spaces.
			buf.B = append(buf.B, '"')
			buf.B = append(buf.B, str...)
			buf.B = append(buf.B, '"')
			return
		}

		if format.ContainsDoubleQuote(str) {
			// Contains double quote, double quote
			// and append escaped existing.
			buf.B = append(buf.B, '"')
			buf.B = format.AppendEscape(buf.B, str)
			buf.B = append(buf.B, '"')
			return
		}
	}

	// Double quoted, enclosed in braces, or
	// literally anything else: append as-is.
	buf.B = append(buf.B, str...)
	return
}

// AppendQuoteValue will append (and escape/quote where necessary) a formatted value string.
func AppendQuoteValue(buf *byteutil.Buffer, str string) {
	switch {
	case len(str) == 0:
		// Append empty quotes.
		buf.B = append(buf.B, `""`...)
		return

	case len(str) == 1:
		// Append quote single byte.
		buf.B = append(buf.B, '\'')
		buf.B = append(buf.B, format.Byte2Str(str[0])...)
		buf.B = append(buf.B, '\'')
		return

	case len(str) > format.SingleTermLine || !format.IsSafeASCII(str):
		// Long line or contains non-ascii chars.
		buf.B = strconv.AppendQuote(buf.B, str)
		return

	case !isQuoted(str):
		// Not single/double quoted already.

		// Get space / tab indices (if any).
		s := strings.IndexByte(str, ' ')
		t := strings.IndexByte(str, '\t')

		// Find first whitespace.
		sp0 := smallest(s, t)
		if sp0 < 0 {
			break
		}

		// Check if str is enclosed by braces.
		// (but without any key-value separator).
		if (enclosedBy(str, sp0, '{', '}') ||
			enclosedBy(str, sp0, '[', ']') ||
			enclosedBy(str, sp0, '(', ')')) &&
			strings.IndexByte(str, '=') < 0 {
			break
		}

		if format.ContainsDoubleQuote(str) {
			// Contains double quote, double quote
			// and append escaped existing.
			buf.B = append(buf.B, '"')
			buf.B = format.AppendEscape(buf.B, str)
			buf.B = append(buf.B, '"')
			return
		}

		// Quote un-enclosed spaces.
		buf.B = append(buf.B, '"')
		buf.B = append(buf.B, str...)
		buf.B = append(buf.B, '"')
		return
	}

	// Double quoted, enclosed in braces, or
	// literally anything else: append as-is.
	buf.B = append(buf.B, str...)
	return
}

// isQuoted checks if string is single or double quoted.
func isQuoted(str string) bool {
	return (str[0] == '"' && str[len(str)-1] == '"') ||
		(str[0] == '\'' && str[len(str)-1] == '\'')
}

// smallest attempts to return the smallest positive value of those given.
func smallest(i1, i2 int) int {
	if i1 >= 0 && (i2 < 0 || i1 < i2) {
		return i1
	}
	return i2
}

// enclosedBy will check if given string is enclosed by end, and at least non-whitespace up to start.
func enclosedBy(str string, sp int, start, end byte) bool {
	// Check for ending char in string.
	if str[len(str)-1] != end {
		return false
	}

	// Check for starting char in string.
	i := strings.IndexByte(str, start)
	if i < 0 {
		return false
	}

	// Check before space.
	return i < sp
}