1
2
3
4
5
6 package d2plugin
7
8 import (
9 "context"
10 "encoding/json"
11 "os/exec"
12
13 "oss.terrastruct.com/util-go/xexec"
14 "oss.terrastruct.com/util-go/xmain"
15
16 "oss.terrastruct.com/d2/d2graph"
17 )
18
19
20
21
22 var plugins []Plugin
23
24 type PluginSpecificFlag struct {
25 Name string
26 Type string
27 Default interface{}
28 Usage string
29
30 Tag string
31 }
32
33 func (f *PluginSpecificFlag) AddToOpts(opts *xmain.Opts) {
34 switch f.Type {
35 case "string":
36 opts.String("", f.Name, "", f.Default.(string), f.Usage)
37 case "int64":
38 var val int64
39 switch defaultType := f.Default.(type) {
40 case int64:
41 val = defaultType
42 case float64:
43
44 val = int64(defaultType)
45 }
46 opts.Int64("", f.Name, "", val, f.Usage)
47 case "[]int64":
48 var slice []int64
49 switch defaultType := f.Default.(type) {
50 case []int64:
51 slice = defaultType
52 case []interface{}:
53 for _, v := range defaultType {
54 switch defaultType := v.(type) {
55 case int64:
56 slice = append(slice, defaultType)
57 case float64:
58
59 slice = append(slice, int64(defaultType))
60 }
61 }
62 }
63 opts.Int64Slice("", f.Name, "", slice, f.Usage)
64 }
65 }
66
67 type Plugin interface {
68
69 Info(context.Context) (*PluginInfo, error)
70
71 Flags(context.Context) ([]PluginSpecificFlag, error)
72
73 HydrateOpts([]byte) error
74
75
76
77 Layout(context.Context, *d2graph.Graph) error
78
79
80 PostProcess(context.Context, []byte) ([]byte, error)
81 }
82
83 type RoutingPlugin interface {
84
85 RouteEdges(context.Context, *d2graph.Graph, []*d2graph.Edge) error
86 }
87
88
89
90
91 type PluginInfo struct {
92 Name string `json:"name"`
93 ShortHelp string `json:"shortHelp"`
94 LongHelp string `json:"longHelp"`
95
96
97
98
99 Type string `json:"type"`
100
101 Path string `json:"path"`
102
103 Features []PluginFeature `json:"features"`
104 }
105
106 const binaryPrefix = "d2plugin-"
107
108 func ListPlugins(ctx context.Context) ([]Plugin, error) {
109
110
111
112
113
114
115 var ps []Plugin
116 ps = append(ps, plugins...)
117
118 matches, err := xexec.SearchPath(binaryPrefix)
119 if err != nil {
120 return nil, err
121 }
122 BINARY_PLUGINS_LOOP:
123 for _, path := range matches {
124 p := &execPlugin{path: path}
125 info, err := p.Info(ctx)
126 if err != nil {
127 return nil, err
128 }
129 for _, p2 := range ps {
130 info2, err := p2.Info(ctx)
131 if err != nil {
132 return nil, err
133 }
134 if info.Name == info2.Name {
135 continue BINARY_PLUGINS_LOOP
136 }
137 }
138 ps = append(ps, p)
139 }
140 return ps, nil
141 }
142
143 func ListPluginInfos(ctx context.Context, ps []Plugin) ([]*PluginInfo, error) {
144 var infoSlice []*PluginInfo
145 for _, p := range ps {
146 info, err := p.Info(ctx)
147 if err != nil {
148 return nil, err
149 }
150 infoSlice = append(infoSlice, info)
151 }
152
153 return infoSlice, nil
154 }
155
156
157
158
159
160
161
162 func FindPlugin(ctx context.Context, ps []Plugin, name string) (Plugin, error) {
163 for _, p := range ps {
164 info, err := p.Info(ctx)
165 if err != nil {
166 return nil, err
167 }
168 if info.Name == name {
169 return p, nil
170 }
171 }
172 return nil, exec.ErrNotFound
173 }
174
175 func ListPluginFlags(ctx context.Context, ps []Plugin) ([]PluginSpecificFlag, error) {
176 var out []PluginSpecificFlag
177 for _, p := range ps {
178 flags, err := p.Flags(ctx)
179 if err != nil {
180 return nil, err
181 }
182 out = append(out, flags...)
183 }
184
185 return out, nil
186 }
187
188 func HydratePluginOpts(ctx context.Context, ms *xmain.State, plugin Plugin) error {
189 opts := make(map[string]interface{})
190 flags, err := plugin.Flags(ctx)
191 if err != nil {
192 return err
193 }
194 for _, f := range flags {
195 switch f.Type {
196 case "string":
197 val, _ := ms.Opts.Flags.GetString(f.Name)
198 opts[f.Tag] = val
199 case "int64":
200 val, _ := ms.Opts.Flags.GetInt64(f.Name)
201 opts[f.Tag] = val
202 case "[]int64":
203 val, _ := ms.Opts.Flags.GetInt64Slice(f.Name)
204 opts[f.Tag] = val
205 }
206 }
207
208 b, err := json.Marshal(opts)
209 if err != nil {
210 return err
211 }
212
213 return plugin.HydrateOpts(b)
214 }
215
View as plain text