File: clause.go

package info (click to toggle)
incus 6.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,392 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (117 lines) | stat: -rw-r--r-- 2,414 bytes parent folder | download | duplicates (3)
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
package filter

import (
	"errors"
	"regexp"
	"slices"
	"strings"
)

// Clause is a single filter clause in a filter string.
type Clause struct {
	PrevLogical string
	Not         bool
	Field       string
	Operator    string
	Value       string
}

// ClauseSet is a set of clauses. There are configurable functions that can be used to
// perform unique parsing of the clauses.
type ClauseSet struct {
	Clauses []Clause
	Ops     OperatorSet

	ParseInt         func(Clause) (int64, error)
	ParseUint        func(Clause) (uint64, error)
	ParseString      func(Clause) (string, error)
	ParseBool        func(Clause) (bool, error)
	ParseRegexp      func(Clause) (*regexp.Regexp, error)
	ParseStringSlice func(Clause) ([]string, error)
}

// Parse a user-provided filter string.
func Parse(s string, op OperatorSet) (*ClauseSet, error) {
	if !op.isValid() {
		return nil, errors.New("Invalid operator set")
	}

	clauses := []Clause{}

	parts := strings.Fields(s)

	index := 0
	prevLogical := op.And

	for index < len(parts) {
		clause := Clause{}

		if strings.EqualFold(parts[index], op.Negate) {
			clause.Not = true
			index++
			if index == len(parts) {
				return nil, errors.New("incomplete not clause")
			}
		} else {
			clause.Not = false
		}

		clause.Field = parts[index]

		index++
		if index == len(parts) {
			return nil, errors.New("clause has no operator")
		}

		clause.Operator = parts[index]

		index++
		if index == len(parts) {
			return nil, errors.New("clause has no value")
		}

		value := parts[index]

		// support strings with spaces that are quoted
		for _, symbol := range op.Quote {
			if strings.HasPrefix(value, symbol) {
				value = value[1:]
				for {
					index++
					if index == len(parts) {
						return nil, errors.New("unterminated quote")
					}

					if strings.HasSuffix(parts[index], symbol) {
						break
					}

					value += " " + parts[index]
				}

				end := parts[index]
				value += " " + end[0:len(end)-1]
			}
		}

		clause.Value = value
		index++

		clause.PrevLogical = prevLogical
		if index < len(parts) {
			prevLogical = parts[index]
			if !slices.Contains([]string{op.And, op.Or}, prevLogical) {
				return nil, errors.New("invalid clause composition")
			}

			index++
			if index == len(parts) {
				return nil, errors.New("unterminated compound clause")
			}
		}

		clauses = append(clauses, clause)
	}

	return &ClauseSet{Clauses: clauses, Ops: op}, nil
}