1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package openapi
16
17 import (
18 "fmt"
19 "strings"
20
21 "cuelang.org/go/cue"
22 "cuelang.org/go/cue/ast"
23 "cuelang.org/go/cue/errors"
24 "cuelang.org/go/cue/token"
25 cuejson "cuelang.org/go/encoding/json"
26 internaljson "cuelang.org/go/internal/encoding/json"
27 )
28
29
30 type Config struct {
31
32 PkgName string
33
34
35
36
37 Info interface{}
38
39
40
41
42
43
44
45
46
47 ReferenceFunc func(inst *cue.Instance, path []string) string
48
49
50
51
52
53
54
55
56
57 NameFunc func(val cue.Value, path cue.Path) string
58
59
60
61
62
63 DescriptionFunc func(v cue.Value) string
64
65
66
67 SelfContained bool
68
69
70 Version string
71
72
73
74
75
76
77
78 FieldFilter string
79
80
81
82
83 ExpandReferences bool
84 }
85
86 type Generator = Config
87
88
89
90 func Gen(inst cue.InstanceOrValue, c *Config) ([]byte, error) {
91 if c == nil {
92 c = defaultConfig
93 }
94 all, err := c.All(inst)
95 if err != nil {
96 return nil, err
97 }
98 return internaljson.Marshal(all)
99 }
100
101
102
103
104
105 func Generate(inst cue.InstanceOrValue, c *Config) (*ast.File, error) {
106 all, err := schemas(c, inst)
107 if err != nil {
108 return nil, err
109 }
110 top, err := c.compose(inst, all)
111 if err != nil {
112 return nil, err
113 }
114 return &ast.File{Decls: top.Elts}, nil
115 }
116
117
118
119
120
121 func (g *Generator) All(inst cue.InstanceOrValue) (*OrderedMap, error) {
122 all, err := schemas(g, inst)
123 if err != nil {
124 return nil, err
125 }
126 top, err := g.compose(inst, all)
127 return (*OrderedMap)(top), err
128 }
129
130 func toCUE(name string, x interface{}) (v ast.Expr, err error) {
131 b, err := internaljson.Marshal(x)
132 if err == nil {
133 v, err = cuejson.Extract(name, b)
134 }
135 if err != nil {
136 return nil, errors.Wrapf(err, token.NoPos,
137 "openapi: could not encode %s", name)
138 }
139 return v, nil
140
141 }
142
143 func (c *Config) compose(inst cue.InstanceOrValue, schemas *ast.StructLit) (x *ast.StructLit, err error) {
144 val := inst.Value()
145 var errs errors.Error
146
147 var title, version string
148 var info *ast.StructLit
149
150 for i, _ := val.Fields(cue.Definitions(true)); i.Next(); {
151 if i.IsDefinition() {
152 continue
153 }
154 label := i.Label()
155 attr := i.Value().Attribute("openapi")
156 if s, _ := attr.String(0); s != "" {
157 label = s
158 }
159 switch label {
160 case "$version":
161 case "-":
162 case "info":
163 info, _ = i.Value().Syntax().(*ast.StructLit)
164 if info == nil {
165 errs = errors.Append(errs, errors.Newf(i.Value().Pos(),
166 "info must be a struct"))
167 }
168 title, _ = i.Value().Lookup("title").String()
169 version, _ = i.Value().Lookup("version").String()
170
171 default:
172 errs = errors.Append(errs, errors.Newf(i.Value().Pos(),
173 "openapi: unsupported top-level field %q", label))
174 }
175 }
176
177
178 switch x := c.Info.(type) {
179 case nil:
180 if title == "" {
181 title = "Generated by cue."
182 for _, d := range val.Doc() {
183 title = strings.TrimSpace(d.Text())
184 break
185 }
186 }
187
188 if version == "" {
189 version, _ = val.Lookup("$version").String()
190 if version == "" {
191 version = "no version"
192 }
193 }
194
195 if info == nil {
196 info = ast.NewStruct(
197 "title", ast.NewString(title),
198 "version", ast.NewString(version),
199 )
200 } else {
201 m := (*OrderedMap)(info)
202 m.Set("title", ast.NewString(title))
203 m.Set("version", ast.NewString(version))
204 }
205
206 case *ast.StructLit:
207 info = x
208 case *OrderedMap:
209 info = (*ast.StructLit)(x)
210 case OrderedMap:
211 info = (*ast.StructLit)(&x)
212 default:
213 x, err := toCUE("info section", x)
214 if err != nil {
215 return nil, err
216 }
217 var ok bool
218 info, ok = x.(*ast.StructLit)
219 if !ok {
220 errs = errors.Append(errs, errors.Newf(token.NoPos,
221 "Info field supplied must marshal to a struct but got %s", fmt.Sprintf("%T", x)))
222 }
223 }
224
225 return ast.NewStruct(
226 "openapi", ast.NewString(c.Version),
227 "info", info,
228 "paths", ast.NewStruct(),
229 "components", ast.NewStruct("schemas", schemas),
230 ), errs
231 }
232
233
234 func (g *Generator) Schemas(inst cue.InstanceOrValue) (*OrderedMap, error) {
235 comps, err := schemas(g, inst)
236 if err != nil {
237 return nil, err
238 }
239 return (*OrderedMap)(comps), err
240 }
241
242 var defaultConfig = &Config{}
243
244
245
246
247
248
249
250
251
252
253
View as plain text