-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
type.go
99 lines (90 loc) · 2.23 KB
/
type.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
package tl
import (
"errors"
"fmt"
"strings"
)
// Type of a Definition or a Parameter.
type Type struct {
Namespace []string `json:"namespace,omitempty"` // namespace components of the type
Name string `json:"name,omitempty"` // the name of the type
Bare bool `json:"bare,omitempty"` // whether this type is bare or boxed
Percent bool `json:"percent,omitempty"` // whether this type has percent in name (like %Message)
GenericRef bool `json:"generic_ref,omitempty"` // whether the type name refers to a generic definition
GenericArg *Type `json:"generic_arg,omitempty"` // generic arguments of the type
}
func (p Type) String() string {
var b strings.Builder
if p.Percent {
b.WriteByte('%')
}
if p.GenericRef {
b.WriteByte('!')
}
for _, ns := range p.Namespace {
b.WriteString(ns)
b.WriteByte('.')
}
b.WriteString(p.Name)
if p.GenericArg != nil {
b.WriteByte('<')
b.WriteString(p.GenericArg.String())
b.WriteByte('>')
}
return b.String()
}
func (p *Type) Parse(s string) error {
if len(s) < 1 {
return errors.New("got empty string")
}
switch s[0] {
case '.':
return errors.New("type can't start with dot")
case '%':
p.Bare = true
p.Percent = true
s = s[1:]
case '!':
p.GenericRef = true
s = s[1:]
}
// Parse `type<generic_arg>`.
if pos := strings.Index(s, "<"); pos >= 0 {
if !strings.HasSuffix(s, ">") {
return errors.New("invalid generic")
}
p.GenericArg = &Type{}
if err := p.GenericArg.Parse(s[pos+1 : len(s)-1]); err != nil {
return fmt.Errorf("failed to parse generic: %w", err)
}
s = s[:pos]
}
// Parse `ns1.ns2.name`.
ns := strings.Split(s, ".")
if len(ns) == 1 {
p.Name = ns[0]
} else {
p.Name = ns[len(ns)-1]
p.Namespace = ns[:len(ns)-1]
}
if p.Name == "" {
return errors.New("blank name")
}
if !isValidName(p.Name) {
return fmt.Errorf("invalid name %q", p.Name)
}
for _, ns := range p.Namespace {
if !isValidName(ns) {
return fmt.Errorf("invalid namespace part %q", ns)
}
}
// Bare types starts from lowercase.
if p.Name != "" && !p.Percent {
p.Bare = p.Name[0:1] == strings.ToLower(p.Name[0:1])
}
return nil
}
// Compile-time interface implementation assertion.
var (
_ fmt.Stringer = Type{}
)