-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathapplication.go
103 lines (85 loc) · 3.2 KB
/
application.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
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package names
import (
"regexp"
"strings"
"unicode"
"github.com/juju/errors"
)
// ApplicationTagKind defines a tag for identifying applications.
const ApplicationTagKind = "application"
const (
// ApplicationSnippet is a non-compiled regexp that can be composed with
// other snippets to form a valid application regexp.
ApplicationSnippet = "(?:[a-z][a-z0-9]*(?:-[a-z0-9]*[a-z][a-z0-9]*)*)"
)
var (
validApplication = regexp.MustCompile("^" + ApplicationSnippet + "$")
tailNumberSuffix = regexp.MustCompile("-[0-9]+$")
)
// IsValidApplication returns whether name is a valid application name.
func IsValidApplication(name string) bool {
return validApplication.MatchString(name)
}
// ValidateApplicationName takes a name and attempts to validate the application
// name, before returning a reason why it's not valid.
//
// This should supersede IsValidApplication. It provides a lot more granular
// information about why something might be wrong, which is a much better UX.
func ValidateApplicationName(name string) error {
if IsValidApplication(name) {
return nil
}
// If the application has uppercase characters, bail out and explain
// why.
if uppercaseChar.MatchString(name) {
return errors.Errorf("invalid application name %q, unexpected uppercase character", name)
}
// If the application ends up being suffixed by a number, then we want
// to mention it to users why.
if tailNumberSuffix.MatchString(name) {
return errors.Errorf("invalid application name %q, unexpected number(s) found after last hyphen", name)
}
index := strings.IndexFunc(name, invalidRuneForApplicationName)
if index < 0 {
return errors.Errorf("invalid application name %q", name)
}
// We have to ensure that we don't break up multi-rune characters, by
// just selecting the index. Instead look at a slice of runes and use
// the first one.
invalidRune := []rune(name[index:])[0]
return errors.Errorf("invalid application name %q, unexpected character %c", name, invalidRune)
}
// invalidRuneForApplicationName works out if there is a valid application rune.
func invalidRuneForApplicationName(r rune) bool {
if (r >= 'a' && r <= 'z') || unicode.IsNumber(r) || r == '-' {
return false
}
return true
}
// ApplicationTag defines a named tagged application.
type ApplicationTag struct {
Name string
}
func (t ApplicationTag) String() string { return t.Kind() + "-" + t.Id() }
// Kind returns the application tag.
func (t ApplicationTag) Kind() string { return ApplicationTagKind }
// Id returns the underlying name of an application as the Id.
func (t ApplicationTag) Id() string { return t.Name }
// NewApplicationTag returns the tag for the application with the given name.
func NewApplicationTag(applicationName string) ApplicationTag {
return ApplicationTag{Name: applicationName}
}
// ParseApplicationTag parses a application tag string.
func ParseApplicationTag(applicationTag string) (ApplicationTag, error) {
tag, err := ParseTag(applicationTag)
if err != nil {
return ApplicationTag{}, err
}
st, ok := tag.(ApplicationTag)
if !ok {
return ApplicationTag{}, invalidTagError(applicationTag, ApplicationTagKind)
}
return st, nil
}