-
Notifications
You must be signed in to change notification settings - Fork 2
/
query.go
116 lines (100 loc) · 2.81 KB
/
query.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
108
109
110
111
112
113
114
115
116
package lib
import (
"fmt"
"net/url"
"reflect"
"regexp"
"strings"
)
func PathEscape(path string) (string, error) {
if path == "nil" {
return "", nil
}
pathParts := strings.Split(path, "/")
newParts := make([]string, len(pathParts))
for i, part := range pathParts {
newParts[i] = url.PathEscape(part)
}
var err error
if len(newParts) > 1 {
path, err = url.JoinPath(newParts[0], newParts[1:]...)
if err != nil {
return path, err
}
} else {
path = newParts[0]
}
return NewUrlPath(path).PruneStartingSlash().String(), nil
}
func BuildPath(resourcePath string, values interface{}) (string, error) {
// Regular expression to find placeholders
r := regexp.MustCompile(`\{(.+?)\}`) // Use non-greedy match to capture individual placeholders
// Iterate over placeholders and replace them
var unreplacedPlaceholders []string
matches := r.FindAllSubmatch([]byte(resourcePath), -1)
for _, match := range matches {
// Extract placeholder name
placeholder := string(match[1])
var value interface{}
var exists bool
if m, ok := values.(map[string]interface{}); ok {
value, exists = m[placeholder]
} else if pathValue, err := findTag(values, "path", placeholder); err == nil {
exists = true
value = pathValue
} else if jsonValue, err := findTag(values, "json", placeholder); err == nil {
exists = true
value = jsonValue
}
if !exists {
// path is allowed to be empty because that can represent the root path.
if placeholder == "path" {
resourcePath = strings.Replace(resourcePath, string(match[0]), "", 1)
} else {
unreplacedPlaceholders = append(unreplacedPlaceholders, placeholder)
}
continue
}
// Convert value to string
var stringValue string
switch v := value.(type) {
case string:
var err error
if placeholder == "path" {
stringValue, err = PathEscape(v)
if err != nil {
return "", err
}
}
default:
stringValue = fmt.Sprintf("%v", v)
}
// Replace placeholder in resourcePath
resourcePath = strings.Replace(resourcePath, string(match[0]), stringValue, 1)
}
// Check if there are unreplaced placeholders
if len(unreplacedPlaceholders) > 0 {
return "", fmt.Errorf("placeholders %v were not replaced", unreplacedPlaceholders)
}
return resourcePath, nil
}
func findTag(s interface{}, tag string, value string) (interface{}, error) {
val := reflect.ValueOf(s)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return "", fmt.Errorf("expected struct or pointer to struct, got %v", val.Kind())
}
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
tag := typ.Field(i).Tag.Get(tag)
if tag != "" && tag == value {
fieldVal := val.Field(i)
if !fieldVal.IsZero() {
return fieldVal.Interface(), nil
}
}
}
return "", fmt.Errorf("`%v` tag not found in struct", tag)
}