1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package openapi
16
17 import (
18 "strings"
19
20 "cuelang.org/go/cue"
21 "cuelang.org/go/cue/ast"
22 "cuelang.org/go/cue/errors"
23 "cuelang.org/go/cue/token"
24 "cuelang.org/go/encoding/jsonschema"
25 "cuelang.org/go/internal"
26 )
27
28
29
30
31
32 func Extract(data cue.InstanceOrValue, c *Config) (*ast.File, error) {
33
34
35
36
37 f := &ast.File{}
38 add := func(d ast.Decl) {
39 if d != nil {
40 f.Decls = append(f.Decls, d)
41 }
42 }
43
44 js, err := jsonschema.Extract(data, &jsonschema.Config{
45 Root: oapiSchemas,
46 Map: openAPIMapping,
47 })
48 if err != nil {
49 return nil, err
50 }
51
52 v := data.Value()
53
54 doc, _ := v.Lookup("info", "title").String()
55 if s, _ := v.Lookup("info", "description").String(); s != "" {
56 doc += "\n\n" + s
57 }
58 cg := internal.NewComment(true, doc)
59
60 if c.PkgName != "" {
61 p := &ast.Package{Name: ast.NewIdent(c.PkgName)}
62 p.AddComment(cg)
63 add(p)
64 } else {
65 add(cg)
66 }
67
68 preamble := js.Preamble()
69 body := js.Decls[len(preamble):]
70 for _, d := range preamble {
71 switch x := d.(type) {
72 case *ast.Package:
73 return nil, errors.Newf(x.Pos(), "unexpected package %q", x.Name.Name)
74
75 default:
76 add(x)
77 }
78 }
79
80
81
82
83
84
85
86
87 if info := v.Lookup("info"); info.Exists() {
88 decls := []interface{}{}
89 if st, ok := info.Syntax().(*ast.StructLit); ok {
90
91 for _, d := range st.Elts {
92 if f, ok := d.(*ast.Field); ok {
93 switch name, _, _ := ast.LabelName(f.Label); name {
94 case "title", "version":
95
96 decls = append(decls, &ast.Field{
97 Label: f.Label,
98 Value: ast.NewBinExpr(token.OR,
99 &ast.UnaryExpr{Op: token.MUL, X: f.Value},
100 ast.NewIdent("string")),
101 })
102 continue
103 }
104 }
105 decls = append(decls, d)
106 }
107 add(&ast.Field{
108 Label: ast.NewIdent("info"),
109 Value: ast.NewStruct(decls...),
110 })
111 }
112 }
113
114 if len(body) > 0 {
115 ast.SetRelPos(body[0], token.NewSection)
116 f.Decls = append(f.Decls, body...)
117 }
118
119 return f, nil
120 }
121
122 const oapiSchemas = "#/components/schemas/"
123
124
125
126 const rootDefs = "#SchemaMap"
127
128 func openAPIMapping(pos token.Pos, a []string) ([]ast.Label, error) {
129 if len(a) != 3 || a[0] != "components" || a[1] != "schemas" {
130 return nil, errors.Newf(pos,
131 `openapi: reference must be of the form %q; found "#/%s"`,
132 oapiSchemas, strings.Join(a, "/"))
133 }
134 name := a[2]
135 if ast.IsValidIdent(name) &&
136 name != rootDefs[1:] &&
137 !internal.IsDefOrHidden(name) {
138 return []ast.Label{ast.NewIdent("#" + name)}, nil
139 }
140 return []ast.Label{ast.NewIdent(rootDefs), ast.NewString(name)}, nil
141 }
142
View as plain text