forked from tinylib/msgp
/
directives.go
130 lines (111 loc) · 2.93 KB
/
directives.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
package parse
import (
"fmt"
"go/ast"
"strings"
"github.com/desertbit/msgp/gen"
)
const linePrefix = "//msgp:"
// func(args, fileset)
type directive func([]string, *FileSet) error
// func(passName, args, printer)
type passDirective func(gen.Method, []string, *gen.Printer) error
// map of all recognized directives
//
// to add a directive, define a func([]string, *FileSet) error
// and then add it to this list.
var directives = map[string]directive{
"shim": applyShim,
"ignore": ignore,
"tuple": astuple,
}
var passDirectives = map[string]passDirective{
"ignore": passignore,
}
func passignore(m gen.Method, text []string, p *gen.Printer) error {
pushstate(m.String())
for _, a := range text {
p.ApplyDirective(m, gen.IgnoreTypename(a))
infof("ignoring %s\n", a)
}
popstate()
return nil
}
// find all comment lines that begin with //msgp:
func yieldComments(c []*ast.CommentGroup) []string {
var out []string
for _, cg := range c {
for _, line := range cg.List {
if strings.HasPrefix(line.Text, linePrefix) {
out = append(out, strings.TrimPrefix(line.Text, linePrefix))
}
}
}
return out
}
//msgp:shim {Type} as:{Newtype} using:{toFunc/fromFunc} mode:{Mode}
func applyShim(text []string, f *FileSet) error {
if len(text) < 4 || len(text) > 5 {
return fmt.Errorf("shim directive should have 3 or 4 arguments; found %d", len(text)-1)
}
name := text[1]
be := gen.Ident(strings.TrimPrefix(strings.TrimSpace(text[2]), "as:")) // parse as::{base}
if name[0] == '*' {
name = name[1:]
be.Needsref(true)
}
be.Alias(name)
usestr := strings.TrimPrefix(strings.TrimSpace(text[3]), "using:") // parse using::{method/method}
methods := strings.Split(usestr, "/")
if len(methods) != 2 {
return fmt.Errorf("expected 2 using::{} methods; found %d (%q)", len(methods), text[3])
}
be.ShimToBase = methods[0]
be.ShimFromBase = methods[1]
if len(text) == 5 {
modestr := strings.TrimPrefix(strings.TrimSpace(text[4]), "mode:") // parse mode::{mode}
switch modestr {
case "cast":
be.ShimMode = gen.Cast
case "convert":
be.ShimMode = gen.Convert
default:
return fmt.Errorf("invalid shim mode; found %s, expected 'cast' or 'convert", modestr)
}
}
infof("%s -> %s\n", name, be.Value.String())
f.findShim(name, be)
return nil
}
//msgp:ignore {TypeA} {TypeB}...
func ignore(text []string, f *FileSet) error {
if len(text) < 2 {
return nil
}
for _, item := range text[1:] {
name := strings.TrimSpace(item)
if _, ok := f.Identities[name]; ok {
delete(f.Identities, name)
infof("ignoring %s\n", name)
}
}
return nil
}
//msgp:tuple {TypeA} {TypeB}...
func astuple(text []string, f *FileSet) error {
if len(text) < 2 {
return nil
}
for _, item := range text[1:] {
name := strings.TrimSpace(item)
if el, ok := f.Identities[name]; ok {
if st, ok := el.(*gen.Struct); ok {
st.AsTuple = true
infof(name)
} else {
warnf("%s: only structs can be tuples\n", name)
}
}
}
return nil
}