/
params.go
107 lines (100 loc) · 2.27 KB
/
params.go
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
package stmt
import (
"unicode"
"github.com/xo/usql/text"
)
// Params holds information about command parameters.
type Params struct {
R []rune
Len int
}
// DecodeParams decodes command parameters.
func DecodeParams(params string) *Params {
r := []rune(params)
return &Params{
R: r,
Len: len(r),
}
}
// GetRaw reads all remaining runes. No substitution or whitespace removal is
// performed.
func (p *Params) GetRaw() string {
s := string(p.R)
p.R, p.Len = p.R[:0], 0
return s
}
// Get reads the next command parameter using the provided substitution func.
// True indicates there are runes remaining in the command parameters to
// process.
func (p *Params) Get(f func(string, bool) (bool, string, error)) (bool, string, error) {
i, _ := findNonSpace(p.R, 0, p.Len)
if i >= p.Len {
return false, "", nil
}
var ok bool
var quote rune
start := i
loop:
for ; i < p.Len; i++ {
c, next := p.R[i], grab(p.R, i+1, p.Len)
switch {
case quote != 0:
start := i - 1
i, ok = readString(p.R, i, p.Len, quote, "")
if !ok {
break loop
}
ok, z, err := f(string(p.R[start:i+1]), false)
switch {
case err != nil:
return false, "", err
case ok:
p.R, p.Len = substitute(p.R, start, p.Len, i-start+1, z)
i = start + len(z) - 1
}
quote = 0
// start of single, double, or backtick string
case c == '\'' || c == '"' || c == '`':
quote = c
case c == ':' && next != ':':
if v := readVar(p.R, i, p.Len); v != nil {
n := v.String()
ok, z, err := f(n[1:], true)
switch {
case err != nil:
return false, "", err
case ok:
p.R, p.Len = substitute(p.R, v.I, p.Len, len(n), z)
i = v.I + len(z) - 1
default:
i += len(n) - 1
}
}
case unicode.IsSpace(c):
break loop
}
}
if quote != 0 {
return false, "", text.ErrUnterminatedQuotedString
}
v := string(p.R[start:i])
p.R = p.R[i:]
p.Len = len(p.R)
return true, v, nil
}
// GetAll retrieves all remaining command parameters using the provided
// substitution func. Will return on the first encountered error.
func (p *Params) GetAll(f func(string, bool) (bool, string, error)) ([]string, error) {
var s []string
for {
ok, v, err := p.Get(f)
if err != nil {
return s, err
}
if !ok {
break
}
s = append(s, v)
}
return s, nil
}