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:
iyear
2023-04-21 16:45:34 +08:00
committed by GitHub
parent dab1618eef
commit 9042ed078b
9 changed files with 68 additions and 45 deletions
+1 -2
View File
@@ -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
+10 -3
View File
@@ -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) {
+3 -1
View File
@@ -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
+5 -2
View File
@@ -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
View File
@@ -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
}
}
}
+26 -13
View File
@@ -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
+2 -6
View File
@@ -18,12 +18,8 @@ BasicType: {
field15: uint64
field16: uint8
field17: rune
field18: {
...
}
field19: {
...
}
field18: _
field19: _
}
TagName: {
f1: string
-4
View File
@@ -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:
-4
View File
@@ -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"