Implement a few CIDLC improvements
* Allow `interface{}` to mean "weakly typed property bag." * Allow slices in IDL types. * Permit the package base as an argument.
This commit is contained in:
parent
df01db5df7
commit
46227870e4
5 changed files with 70 additions and 60 deletions
|
@ -20,6 +20,7 @@ func NewCIDLCCmd() *cobra.Command {
|
|||
var name string
|
||||
var outPack string
|
||||
var outRPC string
|
||||
var pkgBase string
|
||||
var root string
|
||||
var verbose int
|
||||
cmd := &cobra.Command{
|
||||
|
@ -31,6 +32,8 @@ func NewCIDLCCmd() *cobra.Command {
|
|||
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
||||
if name == "" {
|
||||
return errors.New("missing required package name (--name or -n)")
|
||||
} else if pkgBase == "" {
|
||||
return errors.New("missing required package base (--pkg-base or -p)")
|
||||
}
|
||||
if root == "" {
|
||||
root, _ = os.Getwd() // default to the current working directory.
|
||||
|
@ -64,6 +67,8 @@ func NewCIDLCCmd() *cobra.Command {
|
|||
&outPack, "out-pack", "", "Save generated package metadata to this directory")
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&outRPC, "out-rpc", "", "Save generated RPC provider stubs to this directory")
|
||||
cmd.PersistentFlags().StringVarP(
|
||||
&pkgBase, "pkg-base", "p", "", "Specify the package base where this will be published")
|
||||
cmd.PersistentFlags().StringVarP(
|
||||
&root, "root", "r", "", "Pick a different root directory than `pwd` (the default)")
|
||||
cmd.PersistentFlags().IntVarP(
|
||||
|
|
|
@ -291,62 +291,60 @@ func (chk *Checker) CheckStructFields(t *types.TypeName, s *types.Struct,
|
|||
diag.Message("field %v.%v is marked `optional` but is not a pointer in the IDL",
|
||||
t.Name(), fld.Name()))
|
||||
}
|
||||
|
||||
// Only these types are legal:
|
||||
// - Primitives: bool, float64, string
|
||||
// - Other structs
|
||||
// - Pointers to any of the above (if-and-only-if an optional property)
|
||||
// - Pointers to other resource types (capabilities)
|
||||
// - Arrays of the above things
|
||||
// - Maps with string keys and any of the above as values
|
||||
switch ft := fld.Type().(type) {
|
||||
case *types.Basic:
|
||||
if !IsPrimitive(ft) {
|
||||
ok = false
|
||||
cmdutil.Sink().Errorf(
|
||||
diag.Message("field %v.%v is an illegal primitive type %v; must be bool, float64, or string",
|
||||
t.Name(), fld.Name(), ft))
|
||||
}
|
||||
case *types.Named:
|
||||
// TODO: check recursively?
|
||||
switch ut := ft.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
// A named type alias of a primitive type. Ensure it is legal.
|
||||
if !IsPrimitive(ut) {
|
||||
ok = false
|
||||
cmdutil.Sink().Errorf(
|
||||
diag.Message("field %v.%v of type %v is backed by an illegal primitive type %v; "+
|
||||
"must be bool, float64, or string", t.Name(), fld.Name(), ft, ut))
|
||||
}
|
||||
case *types.Struct:
|
||||
// OK so long as it's not a resource (these are required to be pointers).
|
||||
if isres, _ := IsResource(ft.Obj(), ut); isres {
|
||||
ok = false
|
||||
cmdutil.Sink().Errorf(
|
||||
diag.Message("field %v.%v refers to a resource type %v by-value; field must be a pointer",
|
||||
t.Name(), fld.Name(), ft))
|
||||
}
|
||||
default:
|
||||
ok = false
|
||||
cmdutil.Sink().Errorf(
|
||||
diag.Message("field %v.%v is an illegal named field type: %v",
|
||||
t.Name(), fld.Name(), reflect.TypeOf(ut)))
|
||||
}
|
||||
case *types.Pointer:
|
||||
// A pointer is OK so long as the field is either optional or a resource type.
|
||||
if !opts.Optional {
|
||||
if isres, _ := IsResource(nil, ft.Elem()); !isres {
|
||||
ok = false
|
||||
cmdutil.Sink().Errorf(
|
||||
diag.Message("field %v.%v is an illegal pointer; must be optional or of a resource type",
|
||||
t.Name(), fld.Name()))
|
||||
}
|
||||
}
|
||||
default:
|
||||
contract.Failf("Unrecognized field type: %v (type=%v typetype=%v)",
|
||||
fld.Name(), fld.Type(), reflect.TypeOf(fld.Type()))
|
||||
if err := chk.CheckIDLType(fld.Type(), opts); err != nil {
|
||||
ok = false
|
||||
cmdutil.Sink().Errorf(
|
||||
diag.Message("field %v.%v is an not a legal IDL type: %v", t.Name(), fld.Name(), err))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok, allprops, allopts
|
||||
}
|
||||
|
||||
func (chk *Checker) CheckIDLType(t types.Type, opts PropertyOptions) error {
|
||||
// Only these types are legal:
|
||||
// - Primitives: bool, float64, string
|
||||
// - Other structs
|
||||
// - Pointers to any of the above (if-and-only-if an optional property)
|
||||
// - Pointers to other resource types (capabilities)
|
||||
// - Arrays of the above things
|
||||
// - Maps with string keys and any of the above as values
|
||||
switch ft := t.(type) {
|
||||
case *types.Basic:
|
||||
if !IsPrimitive(ft) {
|
||||
return errors.Errorf("bad primitive type %v; must be bool, float64, or string", ft)
|
||||
}
|
||||
case *types.Interface:
|
||||
// interface{} is fine and is interpreted as a weakly typed map.
|
||||
return nil
|
||||
case *types.Named:
|
||||
switch ut := ft.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
// A named type alias of a primitive type. Ensure it is legal.
|
||||
if !IsPrimitive(ut) {
|
||||
return errors.Errorf(
|
||||
"typedef %v backed by bad primitive type %v; must be bool, float64, or string", ft, ut)
|
||||
}
|
||||
case *types.Struct:
|
||||
// Struct types are okay so long as they aren't resources (these are required to be pointers).
|
||||
if isres, _ := IsResource(ft.Obj(), ut); isres {
|
||||
return errors.Errorf("resource type %v cannot be referenced by-value; must be a pointer", ft)
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("bad named field type: %v", reflect.TypeOf(ut))
|
||||
}
|
||||
case *types.Pointer:
|
||||
// A pointer is OK so long as the field is either optional or a resource type.
|
||||
if !opts.Optional {
|
||||
if isres, _ := IsResource(nil, ft.Elem()); !isres {
|
||||
return errors.New("bad pointer; must be optional or a resource type")
|
||||
}
|
||||
}
|
||||
case *types.Slice:
|
||||
// A slice is OK so long as its element type is also OK.
|
||||
return chk.CheckIDLType(ft.Elem(), PropertyOptions{})
|
||||
default:
|
||||
contract.Failf("Unrecognized field type %v: %v", t, reflect.TypeOf(t))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ import (
|
|||
|
||||
type CompileOptions struct {
|
||||
Name tokens.PackageName // the package name.
|
||||
Root string // the root package.
|
||||
Root string // the package root on the filesystem.
|
||||
PkgBase string // the base package name.
|
||||
OutPack string // the package output location.
|
||||
OutRPC string // the RPC output location.
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ func Compile(opts CompileOptions, paths ...string) error {
|
|||
}
|
||||
var rpcgen *RPCGenerator
|
||||
if out := opts.OutRPC; out != "" {
|
||||
rpcgen = NewRPCGenerator(opts.Root, out)
|
||||
rpcgen = NewRPCGenerator(opts.Root, opts.PkgBase, out)
|
||||
}
|
||||
for _, pkginfo := range prog.Created {
|
||||
pkg, err := chk.Check(opts.Name, pkginfo)
|
||||
|
|
|
@ -308,6 +308,8 @@ func (g *PackGenerator) GenTypeName(t types.Type) string {
|
|||
default:
|
||||
contract.Failf("Unrecognized GenTypeName basic type: %v", k)
|
||||
}
|
||||
case *types.Interface:
|
||||
return "any"
|
||||
case *types.Named:
|
||||
// If this came from the same package; the imports will have arranged for it to be available by name.
|
||||
obj := u.Obj()
|
||||
|
|
|
@ -17,14 +17,16 @@ import (
|
|||
|
||||
type RPCGenerator struct {
|
||||
Root string
|
||||
PkgBase string
|
||||
Out string
|
||||
Currpkg *Package // the package currently being visited.
|
||||
}
|
||||
|
||||
func NewRPCGenerator(root string, out string) *RPCGenerator {
|
||||
func NewRPCGenerator(root string, pkgbase string, out string) *RPCGenerator {
|
||||
return &RPCGenerator{
|
||||
Root: root,
|
||||
Out: out,
|
||||
Root: root,
|
||||
PkgBase: pkgbase,
|
||||
Out: out,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -397,6 +399,8 @@ func (g *RPCGenerator) GenTypeName(t types.Type) string {
|
|||
default:
|
||||
contract.Failf("Unrecognized GenTypeName basic type: %v", k)
|
||||
}
|
||||
case *types.Interface:
|
||||
return "resource.PropertyMap"
|
||||
case *types.Named:
|
||||
obj := u.Obj()
|
||||
// For resource types, simply emit an ID, since that is what will have been serialized.
|
||||
|
@ -415,7 +419,7 @@ func (g *RPCGenerator) GenTypeName(t types.Type) string {
|
|||
|
||||
// Otherwise, we will need to refer to a qualified import name.
|
||||
// TODO: we will need to generate the right imports before we can emit such names.
|
||||
contract.Failf("Cross-package IDL references not yet supported")
|
||||
contract.Failf("Cross-package IDL references not yet supported: pkg=%v name=%v", pkg, name)
|
||||
case *types.Map:
|
||||
return fmt.Sprintf("map[%v]%v", g.GenTypeName(u.Key()), g.GenTypeName(u.Elem()))
|
||||
case *types.Pointer:
|
||||
|
|
Loading…
Reference in a new issue