Skip to content

Commit 2693641

Browse files
committed
Add url package with helpers for url operations
1 parent 1c3e957 commit 2693641

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

utils/url/url.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package url
2+
3+
import (
4+
"strings"
5+
6+
"github.com/ARM-software/golang-utils/utils/commonerrors"
7+
"github.com/ARM-software/golang-utils/utils/reflection"
8+
)
9+
10+
// HasMatchingPathSegments checks whether two path strings match based on their segments.
11+
func HasMatchingPathSegments(pathA, pathB string) (match bool, err error) {
12+
if reflection.IsEmpty(pathA) {
13+
err = commonerrors.UndefinedVariable("pathA")
14+
return
15+
}
16+
17+
pathASegments := SplitPath(pathA)
18+
pathBSegments := SplitPath(pathB)
19+
if len(pathASegments) != len(pathBSegments) {
20+
return
21+
}
22+
23+
for i := range pathBSegments {
24+
pathBSeg, pathASeg := pathBSegments[i], pathASegments[i]
25+
if pathBSeg != pathASeg {
26+
return
27+
}
28+
}
29+
30+
match = true
31+
return
32+
}
33+
34+
// HasMatchingPathSegmentsWithParams is similar to MatchingPathSegments but considers segments as matching if at least one of them contains a path parameter.
35+
//
36+
// HasMatchingPathSegmentsWithParams("/some/{param}/path", "/some/{param}/path") // true
37+
// HasMatchingPathSegmentsWithParams("/some/abc/path", "/some/{param}/path") // true
38+
// HasMatchingPathSegmentsWithParams("/some/abc/path", "/some/def/path") // false
39+
func HasMatchingPathSegmentsWithParams(pathA, pathB string) (match bool, err error) {
40+
if reflection.IsEmpty(pathA) {
41+
err = commonerrors.UndefinedVariable("pathA")
42+
return
43+
}
44+
45+
pathASegments := SplitPath(pathA)
46+
pathBSegments := SplitPath(pathB)
47+
if len(pathASegments) != len(pathBSegments) {
48+
return
49+
}
50+
51+
for i := range pathBSegments {
52+
pathBSeg, pathASeg := pathBSegments[i], pathASegments[i]
53+
if IsParamSegment(pathASeg) {
54+
if pathBSeg == "" {
55+
return
56+
}
57+
continue
58+
}
59+
60+
if IsParamSegment(pathBSeg) {
61+
if pathASeg == "" {
62+
return
63+
}
64+
continue
65+
}
66+
if pathBSeg != pathASeg {
67+
return
68+
}
69+
}
70+
71+
match = true
72+
return
73+
}
74+
75+
// SplitPath returns a slice of the individual segments that make up the path string. It looks for the default "/" path separator when splitting.
76+
func SplitPath(path string) []string {
77+
return SplitPathWithSeparator(path, "/")
78+
}
79+
80+
// SplitPathWithSeparator is similar to SplitPath but allows for specifying the path separator to look for when splitting.
81+
func SplitPathWithSeparator(path string, separator string) []string {
82+
path = strings.TrimSpace(path)
83+
if path == "" || path == separator {
84+
return nil
85+
}
86+
87+
path = strings.Trim(path, separator)
88+
segments := strings.Split(path, separator)
89+
out := segments[:0]
90+
for _, p := range segments {
91+
if p != "" {
92+
out = append(out, p)
93+
}
94+
}
95+
return out
96+
}
97+
98+
// IsParamSegment checks whether the segment string is a path parameter
99+
func IsParamSegment(segment string) bool {
100+
return len(segment) >= 2 && segment[0] == '{' && segment[len(segment)-1] == '}'
101+
}
102+
103+
// JoinPaths returns a single concatenated path string from the supplied paths and correctly sets the default "/" separator between them.
104+
func JoinPaths(paths ...string) (joinedPath string, err error) {
105+
return JoinPathsWithSeparator("/", paths...)
106+
}
107+
108+
// JoinPathsWithSeparator is similar to JoinPaths but allows for specifying the path separator to use.
109+
func JoinPathsWithSeparator(separator string, paths ...string) (joinedPath string, err error) {
110+
if paths == nil {
111+
err = commonerrors.UndefinedVariable("paths")
112+
return
113+
}
114+
if len(paths) == 0 {
115+
return
116+
}
117+
118+
if len(paths) == 1 {
119+
joinedPath = paths[0]
120+
return
121+
}
122+
123+
joinedPath = paths[0]
124+
for _, p := range paths[1:] {
125+
pathAHasSlashSuffix := strings.HasSuffix(joinedPath, separator)
126+
pathBHasSlashPrefix := strings.HasPrefix(p, separator)
127+
128+
switch {
129+
case pathAHasSlashSuffix && pathBHasSlashPrefix:
130+
joinedPath += p[1:]
131+
case !pathAHasSlashSuffix && !pathBHasSlashPrefix:
132+
joinedPath += separator + p
133+
default:
134+
joinedPath += p
135+
}
136+
}
137+
138+
return
139+
}

utils/url/url_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package url
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestUrl_IsParamSegment(t *testing.T) {
10+
t.Run("true", func(t *testing.T) {
11+
assert.True(t, IsParamSegment("{abc}"))
12+
})
13+
14+
t.Run("false", func(t *testing.T) {
15+
assert.False(t, IsParamSegment("abc"))
16+
})
17+
}

0 commit comments

Comments
 (0)