-
Notifications
You must be signed in to change notification settings - Fork 348
/
parser.go
147 lines (130 loc) · 3.91 KB
/
parser.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
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
package uri
import (
"errors"
"fmt"
"net/url"
"strings"
"github.com/treeverse/lakefs/pkg/validator"
)
const (
LakeFSSchema = "lakefs"
LakeFSSchemaSeparator = "://"
PathSeparator = "/"
)
var (
ErrMalformedURI = errors.New("malformed lakefs uri")
ErrInvalidRepoURI = errors.New("not a valid repo uri")
ErrInvalidRefURI = errors.New("not a valid ref uri")
ErrInvalidBranchURI = errors.New("not a valid branch uri")
ErrInvalidPathURI = errors.New("not a valid path uri")
)
type URI struct {
// Repository is the name of the repository being addressed
Repository string
// Ref represents the reference in the repository (commit, tag, branch, etc.)
Ref string
// Path is a path to an object (or prefix of such) in lakeFS. It *could* be null since there's a difference between
// an empty path ("lakefs://repo/branch/", and no path at all e.g. "lakefs://repo/branch").
// Since path is the only URI part that is allowed to be empty, it is represented as a pointer.
Path *string
}
func (u *URI) IsRepository() bool {
return len(u.Repository) > 0 && len(u.Ref) == 0 && u.Path == nil && validator.ReValidRepositoryID.MatchString(u.Repository)
}
func (u *URI) IsRef() bool {
return len(u.Repository) > 0 && len(u.Ref) > 0 && u.Path == nil && validator.ReValidRepositoryID.MatchString(u.Repository) && validator.ReValidRef.MatchString(u.Ref)
}
func (u *URI) IsBranch() bool {
return len(u.Repository) > 0 && len(u.Ref) > 0 && u.Path == nil && validator.ReValidRepositoryID.MatchString(u.Repository) && validator.ReValidBranchID.MatchString(u.Ref)
}
func (u *URI) IsFullyQualified() bool {
return len(u.Repository) > 0 && len(u.Ref) > 0 && u.Path != nil && validator.ReValidRepositoryID.MatchString(u.Repository) && validator.ReValidRef.MatchString(u.Ref)
}
func (u *URI) GetPath() string {
if u.Path == nil {
return ""
}
return *u.Path
}
// WithRef returns a new URI from u replacing the Reference part with the given ref
func (u *URI) WithRef(ref string) *URI {
return &URI{
Repository: u.Repository,
Ref: ref,
Path: u.Path,
}
}
func (u *URI) String() string {
var buf strings.Builder
buf.WriteString(LakeFSSchema)
buf.WriteString(LakeFSSchemaSeparator)
buf.WriteString(u.Repository)
if len(u.Ref) == 0 {
return buf.String()
}
buf.WriteString(PathSeparator)
buf.WriteString(u.Ref)
if u.Path == nil {
return buf.String()
}
buf.WriteString(PathSeparator)
buf.WriteString(*u.Path)
return buf.String()
}
// ParseWithBaseURI parse URI uses base URI as prefix when set and input doesn't start with lakeFS protocol
func ParseWithBaseURI(s string, baseURI string) (*URI, error) {
if len(baseURI) > 0 && !strings.HasPrefix(s, LakeFSSchema+LakeFSSchemaSeparator) {
s = baseURI + s
}
u, err := Parse(s)
if err != nil {
return nil, fmt.Errorf("parsing %s: %w", s, err)
}
return u, nil
}
func Parse(s string) (*URI, error) {
u, err := url.Parse(s)
if err != nil || u.Scheme != LakeFSSchema || u.User != nil {
return nil, ErrMalformedURI
}
repository := u.Hostname()
if len(repository) == 0 {
return nil, ErrMalformedURI
}
var ref string
var path *string
if len(u.Path) > 0 {
if !strings.HasPrefix(u.Path, PathSeparator) {
return nil, ErrMalformedURI
}
const refAndPathParts = 2
levels := strings.SplitN(u.Path[1:], PathSeparator, refAndPathParts)
if len(levels) == refAndPathParts {
ref = levels[0]
path = &levels[1]
} else if len(levels) == 1 {
ref = levels[0]
}
}
return &URI{
Repository: repository,
Ref: ref,
Path: path,
}, nil
}
func Equals(a, b *URI) bool {
return a.Repository == b.Repository &&
a.Ref == b.Ref &&
// either both contain no path, or both do, and that path is equal
((a.Path == nil && b.Path == nil) || (a.Path != nil && b.Path != nil && *a.Path == *b.Path))
}
func IsValid(str string) bool {
_, err := Parse(str)
return err == nil
}
func Must(u *URI, e error) *URI {
if e != nil {
panic(e)
}
return u
}