diff --git a/pkg/cmd/hgctl/plugin/types/marshal.go b/pkg/cmd/hgctl/plugin/types/marshal.go index e950254e4..bf04fbcd9 100644 --- a/pkg/cmd/hgctl/plugin/types/marshal.go +++ b/pkg/cmd/hgctl/plugin/types/marshal.go @@ -124,7 +124,7 @@ func (s *JSONSchemaPropsOrBool) UnmarshalJSON(data []byte) error { func (s JSONSchemaPropsOrBool) MarshalYAML() (interface{}, error) { if s.Schema != nil { - return yaml.Marshal(s.Schema) + return s.Schema, nil } if s.Schema == nil && !s.Allows { diff --git a/pkg/cmd/hgctl/plugin/types/meta.go b/pkg/cmd/hgctl/plugin/types/meta.go index 03a57cd26..f8dda1bae 100644 --- a/pkg/cmd/hgctl/plugin/types/meta.go +++ b/pkg/cmd/hgctl/plugin/types/meta.go @@ -33,7 +33,7 @@ type WasmPluginMeta struct { Spec WasmPluginSpec `json:"spec" yaml:"spec"` } -func defaultWsamPluginMeta() *WasmPluginMeta { +func defaultWasmPluginMeta() *WasmPluginMeta { return &WasmPluginMeta{ APIVersion: "1.0.0", Info: WasmPluginInfo{ @@ -77,7 +77,7 @@ func ParseGoSrc(dir, model string) (*WasmPluginMeta, error) { if err != nil { return nil, err } - meta := defaultWsamPluginMeta() + meta := defaultWasmPluginMeta() meta.setByConfigModel(m) return meta, nil } @@ -96,26 +96,33 @@ func recursiveSetSchema(model *Model, parent *JSONSchemaProps) (string, *JSONSch } newName := cur.HandleFieldTags(model.Tag, parent, model.Name) if IsArray(model.Type) { - item := NewJSONSchemaProps() - item.Type = GetItemType(cur.Type) cur.Type = "array" - if IsObject(item.Type) { - item.Properties = make(map[string]JSONSchemaProps) - for _, field := range model.Fields { - name, child := recursiveSetSchema(&field, cur) - item.Properties[name] = *child - } - } - cur.Items = &JSONSchemaPropsOrArray{Schema: item} + itemModel := &*model + itemModel.Type = GetItemType(model.Type) + _, itemSchema := recursiveSetSchema(itemModel, nil) + cur.Items = &JSONSchemaPropsOrArray{Schema: itemSchema} + } else if IsMap(model.Type) { + cur.Type = "object" + valueModel := &*model + valueModel.Type = GetValueType(model.Type) + valueModel.Tag = "" + valueModel.Doc = "" + _, valueSchema := recursiveSetSchema(valueModel, nil) + cur.AdditionalProperties = &JSONSchemaPropsOrBool{Schema: valueSchema} } else if IsObject(model.Type) { // type may be `array of object`, and it is handled in the first branch - for _, field := range model.Fields { - name, child := recursiveSetSchema(&field, cur) - cur.Properties[name] = *child - } + cur.Properties = make(map[string]JSONSchemaProps) + recursiveObjectProperties(cur, model) } return newName, cur } +func recursiveObjectProperties(parent *JSONSchemaProps, model *Model) { + for _, field := range model.Fields { + name, child := recursiveSetSchema(&field, parent) + parent.Properties[name] = *child + } +} + func (meta *WasmPluginMeta) setModelAnnotations(comment string) { as := GetAnnotations(comment) for _, a := range as { diff --git a/pkg/cmd/hgctl/plugin/types/model_parser.go b/pkg/cmd/hgctl/plugin/types/model_parser.go index 7e7a6f198..556e1398b 100644 --- a/pkg/cmd/hgctl/plugin/types/model_parser.go +++ b/pkg/cmd/hgctl/plugin/types/model_parser.go @@ -30,6 +30,7 @@ import ( const ( ArrayPrefix = "array of " + MapPrefix = "map of " ObjectSuffix = "object" ) @@ -40,7 +41,23 @@ func IsArray(typ string) bool { // GetItemType returns the item type of array, e.g.: array of int -> int func GetItemType(typ string) string { - return strings.TrimPrefix(typ, ArrayPrefix) + if !IsArray(typ) { + return typ + } + return typ[len(ArrayPrefix):] +} + +// IsMap returns true if the given type is a `map of ` +func IsMap(typ string) bool { + return strings.HasPrefix(typ, MapPrefix) +} + +// GetValueType returns the value type of map, e.g.: map of int -> int +func GetValueType(typ string) string { + if !IsMap(typ) { + return typ + } + return typ[len(MapPrefix):] } // IsObject returns true if the given type is an `object` or an `array of object` @@ -259,7 +276,7 @@ func (p *ModelParser) parseModelFields(model string) (fields []Model, err error) return nil, errors.Wrapf(err, "failed to parse type %q of the field %q", field.Type, fd.Name) } if IsObject(fd.Type) { - subModel, err := p.getModelName(field.Type) + subModel, err := p.doGetModelName(pkgName, field.Type) if err != nil { return nil, errors.Wrapf(err, "failed to get the sub-model name of the field %q with type %q", fd.Name, field.Type) } @@ -313,6 +330,8 @@ func (p *ModelParser) doGetModelName(pkgName string, typ ast.Expr) (string, erro return p.doGetModelName(pkgName, t.X) case *ast.ArrayType: // slice or array return p.doGetModelName(pkgName, t.Elt) + case *ast.MapType: + return p.doGetModelName(pkgName, t.Value) case *ast.SelectorExpr: // . pkg, ok := t.X.(*ast.Ident) if !ok { @@ -339,6 +358,16 @@ func (p *ModelParser) parseFieldType(pkgName string, typ ast.Expr) (string, erro return "", err } return ArrayPrefix + ret, nil + case *ast.MapType: + if keyIdent, ok := t.Key.(*ast.Ident); !ok { + return "", ErrInvalidFieldType + } else if keyIdent.Name != "string" { + return "", ErrInvalidFieldType + } else if ret, err := p.parseFieldType(pkgName, t.Value); err != nil { + return "", err + } else { + return MapPrefix + ret, nil + } case *ast.SelectorExpr: // . pkg, ok := t.X.(*ast.Ident) if !ok { @@ -388,4 +417,5 @@ const ( JsonTypeString JsonType = "string" JsonTypeObject JsonType = "object" JsonTypeArray JsonType = "array" + JsonTypeMap JsonType = "map" )