mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-19 04:26:39 +00:00
Fix: make any as top value in cue (#5893)
* Fix: make any as top value in cue Signed-off-by: iyear <ljyngup@gmail.com> * Feat: support different cue special type in type option Signed-off-by: iyear <ljyngup@gmail.com> * Fix: unit test type option Signed-off-by: iyear <ljyngup@gmail.com> --------- Signed-off-by: iyear <ljyngup@gmail.com>
This commit is contained in:
@@ -28,8 +28,7 @@ Auto generation of CUE schema and docs from Go struct
|
||||
| `byte` | `uint8` |
|
||||
| `uintptr` | `uint64` |
|
||||
| `[]byte` | `bytes` |
|
||||
| `interface{}/any` | `{...}` |
|
||||
| `interface{ ... }` | `_` |
|
||||
| `interface{}/any` | `_` |
|
||||
|
||||
### Map Type
|
||||
|
||||
|
||||
@@ -80,9 +80,16 @@ func (g *Generator) convertDecls(x *goast.GenDecl) (decls []cueast.Decl, _ error
|
||||
}
|
||||
|
||||
func (g *Generator) convert(typ gotypes.Type) (cueast.Expr, error) {
|
||||
// if type is registered as any, return {...}
|
||||
if _, ok := g.opts.anyTypes[typ.String()]; ok {
|
||||
return anyLit(), nil
|
||||
// if type is registered as special type, use it directly
|
||||
if t, ok := g.opts.types[typ.String()]; ok {
|
||||
switch t {
|
||||
case TypeAny:
|
||||
return Ident("_", false), nil
|
||||
case TypeEllipsis:
|
||||
return &cueast.StructLit{Elts: []cueast.Decl{&cueast.Ellipsis{}}}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported special cue type %d", t)
|
||||
}
|
||||
}
|
||||
|
||||
switch t := typ.(type) {
|
||||
|
||||
@@ -33,7 +33,9 @@ func TestConvert(t *testing.T) {
|
||||
|
||||
got := &bytes.Buffer{}
|
||||
decls, err := g.Generate(
|
||||
WithAnyTypes("*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured"),
|
||||
WithTypes(map[string]Type{
|
||||
"*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured": TypeEllipsis,
|
||||
}),
|
||||
WithTypeFilter(func(typ *goast.TypeSpec) bool {
|
||||
if typ.Name == nil {
|
||||
return true
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestNewGenerator(t *testing.T) {
|
||||
|
||||
assert.NotNil(t, g.pkg)
|
||||
assert.NotNil(t, g.types)
|
||||
assert.Equal(t, g.opts.anyTypes, newDefaultOptions().anyTypes)
|
||||
assert.Equal(t, g.opts.types, newDefaultOptions().types)
|
||||
assert.Equal(t, g.opts.nullable, newDefaultOptions().nullable)
|
||||
// assert can't compare function
|
||||
assert.True(t, g.opts.typeFilter(nil))
|
||||
@@ -57,7 +57,10 @@ func TestGeneratorPackage(t *testing.T) {
|
||||
func TestGeneratorGenerate(t *testing.T) {
|
||||
g := testGenerator(t)
|
||||
|
||||
decls, err := g.Generate(WithAnyTypes("foo", "bar"), nil)
|
||||
decls, err := g.Generate(WithTypes(map[string]Type{
|
||||
"foo": TypeAny,
|
||||
"bar": TypeAny,
|
||||
}), nil)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, decls)
|
||||
|
||||
|
||||
+21
-10
@@ -18,8 +18,18 @@ package cuegen
|
||||
|
||||
import goast "go/ast"
|
||||
|
||||
// Type is a special cue type
|
||||
type Type int
|
||||
|
||||
const (
|
||||
// TypeAny converts go type to _(top value) in cue
|
||||
TypeAny Type = iota
|
||||
// TypeEllipsis converts go type to {...} in cue
|
||||
TypeEllipsis
|
||||
)
|
||||
|
||||
type options struct {
|
||||
anyTypes map[string]struct{}
|
||||
types map[string]Type
|
||||
nullable bool
|
||||
typeFilter func(typ *goast.TypeSpec) bool
|
||||
}
|
||||
@@ -29,24 +39,25 @@ type Option func(opts *options)
|
||||
|
||||
func newDefaultOptions() *options {
|
||||
return &options{
|
||||
anyTypes: map[string]struct{}{
|
||||
"map[string]interface{}": {}, "map[string]any": {},
|
||||
"interface{}": {}, "any": {},
|
||||
types: map[string]Type{
|
||||
"map[string]interface{}": TypeEllipsis, "map[string]any": TypeEllipsis,
|
||||
"interface{}": TypeAny, "any": TypeAny,
|
||||
},
|
||||
nullable: false,
|
||||
typeFilter: func(_ *goast.TypeSpec) bool { return true },
|
||||
}
|
||||
}
|
||||
|
||||
// WithAnyTypes appends go types as any type({...}) in CUE
|
||||
// WithTypes appends go types as specified cue types in generation
|
||||
//
|
||||
// Example:*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured
|
||||
// Example:*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured, TypeEllipsis
|
||||
//
|
||||
// Default any types are: map[string]interface{}, map[string]any, interface{}, any
|
||||
func WithAnyTypes(types ...string) Option {
|
||||
// - Default any types: interface{}, any
|
||||
// - Default ellipsis types: map[string]interface{}, map[string]any
|
||||
func WithTypes(types map[string]Type) Option {
|
||||
return func(opts *options) {
|
||||
for _, t := range types {
|
||||
opts.anyTypes[t] = struct{}{}
|
||||
for k, v := range types {
|
||||
opts.types[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,32 +27,45 @@ func TestWithAnyTypes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts []Option
|
||||
extra map[string]struct{}
|
||||
extra map[string]Type
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
opts: nil,
|
||||
extra: map[string]struct{}{},
|
||||
extra: map[string]Type{},
|
||||
},
|
||||
{
|
||||
name: "single",
|
||||
opts: []Option{WithAnyTypes("foo", "bar")},
|
||||
extra: map[string]struct{}{"foo": {}, "bar": {}},
|
||||
name: "single",
|
||||
opts: []Option{WithTypes(map[string]Type{
|
||||
"foo": TypeAny,
|
||||
"bar": TypeEllipsis,
|
||||
})},
|
||||
extra: map[string]Type{"foo": TypeAny, "bar": TypeEllipsis},
|
||||
},
|
||||
{
|
||||
name: "multiple",
|
||||
opts: []Option{WithAnyTypes("foo", "bar"),
|
||||
WithAnyTypes("baz", "qux")},
|
||||
extra: map[string]struct{}{"foo": {}, "bar": {}, "baz": {}, "qux": {}},
|
||||
opts: []Option{WithTypes(map[string]Type{
|
||||
"foo": TypeAny,
|
||||
"bar": TypeEllipsis,
|
||||
}), WithTypes(map[string]Type{
|
||||
"baz": TypeEllipsis,
|
||||
"qux": TypeAny,
|
||||
})},
|
||||
extra: map[string]Type{
|
||||
"foo": TypeAny,
|
||||
"bar": TypeEllipsis,
|
||||
"baz": TypeEllipsis,
|
||||
"qux": TypeAny,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
opts := options{anyTypes: map[string]struct{}{}}
|
||||
opts := options{types: map[string]Type{}}
|
||||
for _, opt := range tt.opts {
|
||||
opt(&opts)
|
||||
}
|
||||
assert.Equal(t, opts.anyTypes, tt.extra, tt.name)
|
||||
assert.Equal(t, opts.types, tt.extra, tt.name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +140,9 @@ func TestWithTypeFilter(t *testing.T) {
|
||||
func TestDefaultOptions(t *testing.T) {
|
||||
opts := newDefaultOptions()
|
||||
|
||||
assert.Equal(t, opts.anyTypes, map[string]struct{}{
|
||||
"map[string]interface{}": {}, "map[string]any": {},
|
||||
"interface{}": {}, "any": {},
|
||||
assert.Equal(t, opts.types, map[string]Type{
|
||||
"map[string]interface{}": TypeEllipsis, "map[string]any": TypeEllipsis,
|
||||
"interface{}": TypeAny, "any": TypeAny,
|
||||
})
|
||||
assert.Equal(t, opts.nullable, false)
|
||||
// assert can't compare function
|
||||
|
||||
Vendored
+2
-6
@@ -18,12 +18,8 @@ BasicType: {
|
||||
field15: uint64
|
||||
field16: uint8
|
||||
field17: rune
|
||||
field18: {
|
||||
...
|
||||
}
|
||||
field19: {
|
||||
...
|
||||
}
|
||||
field18: _
|
||||
field19: _
|
||||
}
|
||||
TagName: {
|
||||
f1: string
|
||||
|
||||
@@ -46,10 +46,6 @@ func basicType(x *gotypes.Basic) cueast.Expr {
|
||||
}
|
||||
}
|
||||
|
||||
func anyLit() cueast.Expr {
|
||||
return &cueast.StructLit{Elts: []cueast.Decl{&cueast.Ellipsis{}}}
|
||||
}
|
||||
|
||||
func basicLabel(t *gotypes.Basic, v string) (cueast.Expr, error) {
|
||||
switch {
|
||||
case t.Info()&gotypes.IsInteger != 0:
|
||||
|
||||
@@ -73,10 +73,6 @@ func TestBasicType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyLit(t *testing.T) {
|
||||
assert.Equal(t, anyLit(), &cueast.StructLit{Elts: []cueast.Decl{&cueast.Ellipsis{}}})
|
||||
}
|
||||
|
||||
func TestBasicLabel(t *testing.T) {
|
||||
overflowInt64 := strconv.FormatInt(math.MaxInt64, 10) + "0"
|
||||
overflowUint64 := strconv.FormatUint(math.MaxUint64, 10) + "0"
|
||||
|
||||
Reference in New Issue
Block a user