encoding/jsonschema: add features to support OpenAPI
- Root: root directory from where to load Schema
- Map: remapping of references
Also:
- Fixed bug in compiler that would reject top-level attributes
- Added internal.NewAttr helper.
Change-Id: I87c79fe49448bddfb873fc9ac805879cfdb10a4b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5250
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go
index 2bac410..09838e6 100644
--- a/encoding/jsonschema/decode.go
+++ b/encoding/jsonschema/decode.go
@@ -52,19 +52,37 @@
return ident
}
-func (d *decoder) decode(inst *cue.Instance) *ast.File {
- root := state{decoder: d}
- expr, state := root.schemaState(inst.Value())
-
- var a []ast.Decl
+func (d *decoder) decode(v cue.Value) *ast.File {
+ f := &ast.File{}
if pkgName := d.cfg.PkgName; pkgName != "" {
pkg := &ast.Package{Name: ast.NewIdent(pkgName)}
- state.doc(pkg)
+ f.Decls = append(f.Decls, pkg)
+ }
- a = append(a, pkg)
- } else if doc := state.comment(); doc != nil {
- a = append(a, doc)
+ var a []ast.Decl
+
+ if d.cfg.Root == "" {
+ a = append(a, d.schema([]string{"Schema"}, v)...)
+ } else {
+ ref := d.parseRef(token.NoPos, d.cfg.Root)
+ if ref == nil {
+ return f
+ }
+ i, err := v.Lookup(ref...).Fields()
+ if err != nil {
+ d.errs = errors.Append(d.errs, errors.Promote(err, ""))
+ return nil
+ }
+ for i.Next() {
+ ref := append(ref, i.Label())
+ ref = d.mapRef(i.Value().Pos(), "", ref)
+ if len(ref) == 0 {
+ return nil
+ }
+ decls := d.schema(ref, i.Value())
+ a = append(a, decls...)
+ }
}
var imports []string
@@ -78,9 +96,23 @@
for _, p := range imports {
x.Specs = append(x.Specs, ast.NewImport(nil, p))
}
- a = append(a, x)
+ f.Decls = append(f.Decls, x)
}
+ f.Decls = append(f.Decls, a...)
+ f.Decls = append(f.Decls, d.definitions...)
+
+ return f
+}
+
+func (d *decoder) schema(ref []string, v cue.Value) (a []ast.Decl) {
+ root := state{decoder: d}
+
+ inner := len(ref) - 1
+ name := ref[inner]
+
+ expr, state := root.schemaState(v)
+
tags := []string{}
if state.jsonschema != "" {
tags = append(tags, fmt.Sprintf("schema=%q", state.jsonschema))
@@ -89,32 +121,45 @@
tags = append(tags, fmt.Sprintf("id=%q", state.id))
}
if len(tags) > 0 {
- a = append(a, addTag("Schema", "jsonschema", strings.Join(tags, ",")))
+ a = append(a, addTag(name, "jsonschema", strings.Join(tags, ",")))
}
if state.deprecated {
- a = append(a, addTag("Schema", "deprecated", ""))
+ a = append(a, addTag(name, "deprecated", ""))
}
f := &ast.Field{
- Label: ast.NewIdent("Schema"),
+ Label: ast.NewIdent(name),
+ Token: token.ISA,
Value: expr,
}
- f.Token = token.ISA
a = append(a, f)
- a = append(a, d.definitions...)
+ state.doc(a[0])
- return &ast.File{Decls: a}
+ for i := inner - 1; i >= 0; i-- {
+ a = []ast.Decl{&ast.Field{
+ Label: ast.NewIdent(ref[i]),
+ Token: token.ISA,
+ Value: &ast.StructLit{Elts: a},
+ }}
+ expr = ast.NewStruct(ref[i], token.ISA, expr)
+ }
+
+ return a
}
func (d *decoder) errf(n cue.Value, format string, args ...interface{}) ast.Expr {
- d.warnf(n, format, args...)
+ d.warnf(n.Pos(), format, args...)
return &ast.BadExpr{From: n.Pos()}
}
-func (d *decoder) warnf(n cue.Value, format string, args ...interface{}) {
- d.errs = errors.Append(d.errs, errors.Newf(n.Pos(), format, args...))
+func (d *decoder) warnf(p token.Pos, format string, args ...interface{}) {
+ d.addErr(errors.Newf(p, format, args...))
+}
+
+func (d *decoder) addErr(err errors.Error) {
+ d.errs = errors.Append(d.errs, err)
}
func (d *decoder) number(n cue.Value) ast.Expr {
@@ -306,7 +351,7 @@
c := constraintMap[key]
if c == nil {
if pass == 0 {
- s.warnf(n, "unsupported constraint %q", key)
+ s.warnf(n.Pos(), "unsupported constraint %q", key)
}
return
}