This repository has been archived by the owner on Apr 20, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathusage.go
91 lines (86 loc) · 3.13 KB
/
usage.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
package sapphire
import (
"errors"
"regexp"
"strings"
)
type UsageTag struct {
Name string // Name of the tag, e.g for <reason:string> the name is reason.
Type string // Type of the tag, e.g for <reason:string> the type is string.
Rest bool // If this is rest of the arguments, e.g for <reason:string...> it is true.
Required bool // If this argument is required, e.g <name> is required but [name] is not.
}
// Parse a usage string into tags.
func ParseUsage(usage string) ([]*UsageTag, error) {
// TODO: We'll need to handle more cases to improve error handling.
tags := make([]*UsageTag, 0)
// The current tag we are parsing and building.
current := &UsageTag{Required: false, Rest: false, Type: "", Name: ""}
// true if we are currently parsing the type, otherwise the name.
typeMode := false
for _, c := range usage {
if c == '<' {
current.Required = true
} else if c == '>' {
// Beginning the tag name with @@ is a syntactic sugar for member and beginning with @ is for user.
// And if the type is missing we assume literal.
if strings.HasPrefix(current.Name, "@@") && current.Type == "" {
current.Name = strings.TrimPrefix(current.Name, "@@")
current.Type = "member"
} else if strings.HasPrefix(current.Name, "@") && current.Type == "" {
current.Name = strings.TrimPrefix(current.Name, "@")
current.Type = "user"
} else if current.Type == "" {
current.Type = "literal"
}
tags = append(tags, current)
current = &UsageTag{Required: false, Rest: false, Type: "", Name: ""}
typeMode = false
} else if c == '[' {
if current.Required {
return tags, errors.New("Cannot open an optional tag after opening a required one.")
}
} else if c == ']' {
if strings.HasPrefix(current.Name, "@@") && current.Type == "" {
current.Name = strings.TrimPrefix(current.Name, "@@")
current.Type = "member"
} else if strings.HasPrefix(current.Name, "@") && current.Type == "" {
current.Name = strings.TrimPrefix(current.Name, "@")
current.Type = "user"
} else if current.Type == "" {
current.Type = "literal"
}
tags = append(tags, current)
current = &UsageTag{Required: false, Rest: false, Type: "", Name: ""}
typeMode = false
} else if c == ' ' {
continue
} else if c == ':' {
typeMode = true
} else {
if typeMode {
current.Type += string(c)
} else {
current.Name += string(c)
}
}
}
// Now that we know enough about the tags and how many are there we can validate rest args.
for i, tag := range tags {
if strings.HasSuffix(tag.Type, "...") {
if i != len(tags)-1 {
return tags, errors.New("Rest parameters can only appear last.")
}
tag.Type = strings.TrimSuffix(tag.Type, "...")
tag.Rest = true
}
}
return tags, nil
}
// HumanizeUsageRegex is the regexp used for HuamnizeUsage
var HumanizeUsageRegex = regexp.MustCompile("(<|\\[)(\\w+):[^.]+?(\\.\\.\\.)?(>|\\])")
// HumanizeUsage removes the unneccessary types and shows only the names.
// e.g <hello:string> <user:user> [rest:int...] => <hello> <user> [rest...]
func HumanizeUsage(usage string) string {
return HumanizeUsageRegex.ReplaceAllString(usage, "$1$2$3$4")
}