func (en *Encoder) writeStruct(spec *ast.StructType, name string) { for _, field := range spec.Fields.List { tag := reflect.StructTag("") if field.Tag != nil { tag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]) } var c string if tag.Get("if") != "" && tag.Get("noreplace") != "true" { c = strings.Replace(tag.Get("if"), ".", name+".", -1) } if c != "" { fmt.Fprintf(en.buf, "if %s {\n", c) } for _, n := range field.Names { en.writeType(field.Type, fmt.Sprintf("%s.%s", name, n), tag) } if c != "" { fmt.Fprint(en.buf, "}\n") } } }
func (w *writing) writeStruct(spec *ast.StructType, name string) { var lastCondition conditions for _, field := range spec.Fields.List { tag := reflect.StructTag("") if field.Tag != nil { tag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]) } var condition conditions if ifTag := tag.Get("if"); ifTag != "" { condition = parseCondition(ifTag) } if !lastCondition.equals(condition) { if lastCondition != nil { w.buf.WriteString("}\n") } if condition != nil { condition.print(name, &w.buf) } } lastCondition = condition for _, n := range field.Names { w.writeType(field.Type, fmt.Sprintf("%s.%s", name, n), tag) } } if lastCondition != nil { w.buf.WriteString("}\n") } }
// checkCanonicalFieldTag checks a single struct field tag. func checkCanonicalFieldTag(f *File, field *ast.Field, seen *map[[2]string]token.Pos) { if field.Tag == nil { return } tag, err := strconv.Unquote(field.Tag.Value) if err != nil { f.Badf(field.Pos(), "unable to read struct tag %s", field.Tag.Value) return } if err := validateStructTag(tag); err != nil { raw, _ := strconv.Unquote(field.Tag.Value) // field.Tag.Value is known to be a quoted string f.Badf(field.Pos(), "struct field tag %#q not compatible with reflect.StructTag.Get: %s", raw, err) } for _, key := range checkTagDups { val := reflect.StructTag(tag).Get(key) if val == "" || val == "-" || val[0] == ',' { continue } if i := strings.Index(val, ","); i >= 0 { val = val[:i] } if *seen == nil { *seen = map[[2]string]token.Pos{} } if pos, ok := (*seen)[[2]string{key, val}]; ok { f.Badf(field.Pos(), "struct field %s repeats %s tag %q also at %s", field.Names[0].Name, key, val, f.loc(pos)) } else { (*seen)[[2]string{key, val}] = field.Pos() } } // Check for use of json or xml tags with unexported fields. // Embedded struct. Nothing to do for now, but that // may change, depending on what happens with issue 7363. if len(field.Names) == 0 { return } if field.Names[0].IsExported() { return } for _, enc := range [...]string{"json", "xml"} { if reflect.StructTag(tag).Get(enc) != "" { f.Badf(field.Pos(), "struct field %s has %s tag but is not exported", field.Names[0].Name, enc) return } } }
func typeSchema(title string, t reflect.Type, tag reflect.StructTag) *Value { fieldType := fieldType(t, tag) isReadOnly := tag.Get("readOnly") != "" value := &Value{ Title: title, Type: fieldType, Required: isRequired(tag), Enum: enum(tag), Description: tag.Get("description"), Format: Format(tag.Get("format")), ReadOnly: isReadOnly, } switch fieldType { case Map: value.Title = t.Name() value.Format = "tabs" // map items value.Items = &Value{ Type: Object, HeaderTemplate: "{{self.key}}", Properties: map[string]*Value{ "key": typeSchema("key", t.Key(), reflect.StructTag("")), "value": typeSchema("value", t.Elem(), reflect.StructTag("")), }, } // the enum annotation for maps is for the key not for the map itself value.Items.Properties["key"].Enum = enum(tag) value.Enum = make([]string, 0) // if there is valueEnum then the value is considered enum if tag.Get("valueEnum") != "" { value.Items.Properties["value"].Enum = strings.Split(tag.Get("valueEnum"), ",") } else { value.Items.Properties["value"].Enum = make([]string, 0) } case Array: value.Items = typeSchema(t.Name(), t.Elem(), tag) value.Items.HeaderTemplate = tag.Get("headerTemplate") case ProtoEnum: value.Enum = getProtoEnumValues(t) case Object: value = structSchema(title, t, tag) } return value }
func (this Injector) Apply(target interface{}) { elem := reflect.ValueOf(target) for elem.Kind() == reflect.Ptr { elem = elem.Elem() } //fmt.Println(elem, elem.Kind()) if elem.Kind() != reflect.Struct { return } //fmt.Println(elem, elem.NumField()) t := elem.Type() fieldTagMap := make(map[reflect.StructTag]string) for i := 0; i < elem.NumField(); i++ { structField := t.Field(i) //fmt.Println(elem.Field(i).CanSet()) if elem.Field(i).CanSet() { fieldTagMap[structField.Tag] = structField.Name } } for key, value := range this.valueMap { for k, v := range fieldTagMap { if k == reflect.StructTag(key) { elem.FieldByName(v).Set(reflect.ValueOf(value)) break } } } }
func toTable(typeInfo *genbase.TypeInfo) (*Table, error) { table := new(Table) table.PackageName = typeInfo.FileInfo.Name.Name comment := typeInfo.AnnotatedComment.Text tableName := strings.TrimPrefix(comment, "//+table: ") table.Name = tableName table.StructName = typeInfo.Name() structType, err := typeInfo.StructType() if err != nil { return nil, err } fieldInfos := structType.FieldInfos() for _, fieldInfo := range fieldInfos { tagText := fieldInfo.Tag.Value[1 : len(fieldInfo.Tag.Value)-1] tag := reflect.StructTag(tagText) columnInfo := tag.Get("db") columnMaps := strings.Split(columnInfo, ",") columnName := columnMaps[0] isPk := false for _, cm := range columnMaps { if cm == "primarykey" { isPk = true break } } column := Column{FieldInfo: fieldInfo, Name: columnName, IsPk: isPk} table.AddColumn(column) } return table, nil }
func getFlag(field reflect.StructField) (ths flagSpec) { ftlen := len(field.Tag) if ftlen > 0 && string(field.Tag[ftlen-1]) == "!" { if ftlen > 1 && string(field.Tag[ftlen-2]) != "\\" { field.Tag = field.Tag[:ftlen-1] ths.imperitive = true } else if ftlen > 2 { field.Tag = reflect.StructTag(string(field.Tag[:len(field.Tag)-2]) + "!") } else { ths.imperitive = true } } parts := strings.Split(string(field.Tag), ";") switch useName := true; len(parts) { case 3: useName = false ths.names = strings.Split(parts[2], ",") fallthrough case 2: ths.deflt = parts[1] fallthrough case 1: ths.usage = parts[0] fallthrough case 0: ths.typ = field.Type if useName { ths.names = append(ths.names, strings.ToLower(field.Name)) } default: panic("Too many fields!") } return }
// Creates a new resource // Receives the object to be mappen in a new Resource // and receive the field name and field tag as optional arguments func NewResource(object interface{}, args ...string) *Resource { value := reflect.ValueOf(object) name := value.Type().Name() tag := "" // Defining a name as an opitional secound argument if len(args) >= 1 { name = args[0] } // Defining a tag as an opitional thrid argument if len(args) >= 2 { tag = args[1] } field := reflect.StructField{ Name: name, Tag: reflect.StructTag(tag), Anonymous: false, } //log.Printf("field: %#v\n", field) return scanStruct(value, field, nil) }
// hasOptionalTag returns true if the member has +optional in its comments or // omitempty in its json tags. func hasOptionalTag(m *types.Member) bool { hasOptionalCommentTag := types.ExtractCommentTags( "+", m.CommentLines)[tagOptional] != nil hasOptionalJsonTag := strings.Contains( reflect.StructTag(m.Tags).Get("json"), "omitempty") return hasOptionalCommentTag || hasOptionalJsonTag }
func (b *xmlBuilder) buildValue(value reflect.Value, current *XMLNode, tag reflect.StructTag) error { value = elemOf(value) if !value.IsValid() { // no need to handle zero values return nil } else if tag.Get("location") != "" { // don't handle non-body location values return nil } t := tag.Get("type") if t == "" { switch value.Kind() { case reflect.Struct: t = "structure" case reflect.Slice: t = "list" case reflect.Map: t = "map" } } switch t { case "structure": if field, ok := value.Type().FieldByName("SDKShapeTraits"); ok { tag = tag + reflect.StructTag(" ") + field.Tag } return b.buildStruct(value, current, tag) case "list": return b.buildList(value, current, tag) case "map": return b.buildMap(value, current, tag) default: return b.buildScalar(value, current, tag) } }
//Takes a value of a struct representing a service. func registerService(root string, h interface{}) { if _, ok := h.(GoRestService); !ok { panic(ERROR_INVALID_INTERFACE) } t := reflect.TypeOf(h) if t.Kind() == reflect.Ptr { t = t.Elem() } else { panic(ERROR_INVALID_INTERFACE) } if t.Kind() == reflect.Struct { if field, found := t.FieldByName("RestService"); found { temp := strings.Join(strings.Fields(string(field.Tag)), " ") meta := prepServiceMetaData(root, reflect.StructTag(temp), h, t.Name()) tFullName := _manager().addType(t.PkgPath()+"/"+t.Name(), meta) for i := 0; i < t.NumField(); i++ { f := t.Field(i) mapFieldsToMethods(t, f, tFullName, meta.root) } } return } panic(ERROR_INVALID_INTERFACE) }
func (si *StructInfo) AddField(field *ast.Field) error { if field.Names == nil || len(field.Names) != 1 { return errors.New(fmt.Sprintf("Field contains no name: %v", field)) } jsonName := field.Names[0].Name opts := tagOptions("") if field.Tag != nil { var tagName string // the Tag.Value contains wrapping `` which we slice off here. We hope. v := tagRe.ReplaceAllString(field.Tag.Value, "$1") tag := reflect.StructTag(v).Get("json") tagName, opts = parseTag(tag) if tagName != "" { jsonName = tagName } } si.Fields = append(si.Fields, StructField{ Name: field.Names[0].Name, JsonName: jsonName, OmitEmpty: opts.Contains("omitempty"), ForceString: opts.Contains("string"), }) return nil }
// ExtractArgs parses the arguments out of a template invocation, using // the invoking fields tags. func ExtractArgs(ctx *Context, stp *ast.StructType, name string) ([]string, error) { var found *ast.Field for _, f := range stp.Fields.List { fname, err := nameFromFieldType(ctx, f.Type) if err != nil { return nil, err } if name == fname { found = f } } if found == nil { return nil, errors.New("Couldn't find template invocation: " + name) } if found.Tag == nil { return nil, nil } tag := reflect.StructTag(found.Tag.Value[1 : len(found.Tag.Value)-1]) return strings.Split(tag.Get("template"), ","), nil }
func parseStruct(fset *token.FileSet, s *ast.StructType) *Struct { parsedStruct := &Struct{} if s.Fields.List != nil { parsedStruct.Fields = make([]StructField, 0, len(s.Fields.List)) } for _, field := range s.Fields.List { parsedField := StructField{} for i, name := range field.Names { parsedField.Name += name.Name if i != len(field.Names)-1 { parsedField.Name += ", " } } parsedField.Doc = parseComments(field.Doc) parsedField.Comments = parseComments(field.Comment) if field.Tag != nil { raw := field.Tag.Value parsedField.RawTag = raw if len(raw) >= 2 { // Strip leading/trailing back-ticks: parsedField.Tag = reflect.StructTag(raw[1 : len(raw)-1]) } } parsedField.Type = formatTypeExpr(fset, field.Type) parsedField.StructType = parseEmbeddedStructType(fset, field.Type) parsedStruct.Fields = append(parsedStruct.Fields, parsedField) } return parsedStruct }
func getJsonTags(m *types.Member) []string { jsonTag := reflect.StructTag(m.Tags).Get("json") if jsonTag == "" { return []string{} } return strings.Split(jsonTag, ",") }
// Parse value from string and return position after parsing and error. // This function parses value using PEG parser. // Here: result is pointer to value, // str is string to parse, // params is parsing parameters. // Function returns newLocation - location after the parsed string. On errors err != nil. func Parse(result interface{}, str []byte, params *Options) (newLocation int, err error) { typeOf := reflect.TypeOf(result) valueOf := reflect.ValueOf(result) if typeOf.Kind() != reflect.Ptr { return -1, errors.New("Invalid argument for Parse: waiting for pointer") } if params == nil { params = &Options{SkipWhite: SkipSpaces} } p, err := compile(typeOf.Elem(), reflect.StructTag("")) if err != nil { return -1, err } C := new(parseContext) C.params = params C.str = str C.packrat = make(map[packratKey]*packratValue) C.recursiveLocations = make(map[int]bool) e := Error{str, 0, ""} newLocation = C.parse(valueOf.Elem(), p, 0, &e) if newLocation < 0 { return newLocation, e } return newLocation, nil }
func (p Parser) parseStruct(spec *ast.StructType, attrs *Attrs) (err error) { defer nonTypeEnd(&err) for _, f := range spec.Fields.List { attrs.S.Type = "" attrs.S.Doc = p.parseDoc(f.Doc) if f.Type != nil { attrs.S.Type = fmt.Sprint(f.Type) } for _, n := range f.Names { attrs.S.Field = n.Name attrs.S.Tag = "" if f.Tag != nil { tag, _ := strings2.TrimQuote(f.Tag.Value) attrs.S.Tag = reflect.StructTag(tag) } if err = p.Struct(attrs); err != nil { return } } } if len(spec.Fields.List) == 0 { p.Struct(attrs) } return }
// getJSONTags returns the JSON tags on a field. func getJSONTags(field *ast.Field) []string { var tag string if field.Tag != nil { tag = field.Tag.Value[1 : len(field.Tag.Value)-1] tag = reflect.StructTag(tag).Get("json") } return strings.Split(tag, ",") }
// Copy from the method Get() from reflect.StructTag. But it returns all the // tags defined in the fields by the order that they appear, not the value of // the specific tag. // // The type of the argument tag must be eithor string or reflect.StructTag. // Or panic. func GetAllTags(t interface{}) []TV { var tag reflect.StructTag if _tag, ok := t.(reflect.StructTag); ok { tag = _tag } else if _tag, ok := t.(string); ok { tag = reflect.StructTag(_tag) } else { panic("The type of the argument must be eithor string or reflect.StructTag") } _tags := make([]TV, 0) for tag != "" { // Skip leading space. i := 0 for i < len(tag) && tag[i] == ' ' { i++ } tag = tag[i:] if tag == "" { break } // Scan to colon. A space, a quote or a control character is a syntax error. // Strictly speaking, control chars include the range [0x7f, 0x9f], not just // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters // as it is simpler to inspect the tag's bytes than the tag's runes. i = 0 for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { i++ } if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { break } name := string(tag[:i]) tag = tag[i+1:] // Scan quoted string to find value. i = 1 for i < len(tag) && tag[i] != '"' { if tag[i] == '\\' { i++ } i++ } if i >= len(tag) { break } qvalue := string(tag[:i+1]) tag = tag[i+1:] if value, err := strconv.Unquote(qvalue); err == nil { if strings.TrimSpace(value) != "" { _tags = append(_tags, TV{Tag: name, Value: value}) } } } return _tags }
func ParseStructFileContent(content string) (sdList []StructDescription, prop Property) { var structFirstLineRegex *regexp.Regexp structFirstLineRegex, _ = regexp.Compile("^type ([A-Za-z0-9_]+) struct[ \t]?{") var isStartingParseStruct bool var sd StructDescription content = strings.Replace(content, "\r", "", -1) // lines := strings.Split(content, "\n") lines = removeEmptyLineAndCommentLine(lines) for _, line := range lines { if prop.PackageName == "" { idx := strings.Index(line, "package ") if idx == 0 { prop.PackageName = strings.TrimSpace(line[len("package ")+idx:]) } continue } line = removeCommentPart(line) if !isStartingParseStruct { matched := structFirstLineRegex.FindStringSubmatch(line) if len(matched) >= 2 { sd.Reset() sd.StructName = matched[1] isStartingParseStruct = true } continue } if line == "}" { // 解析完一个struct isStartingParseStruct = false if sd.StructName != "" && len(sd.Fields) != 0 { sdList = append(sdList, sd) sd.Reset() } continue } if isStartingParseStruct { // 在解析field 中 fd := FieldDescriptoin{} tagStartIdx := strings.Index(line, "`") tagEndIdx := strings.LastIndex(line, "`") if tagStartIdx != -1 && tagEndIdx != -1 && tagEndIdx != tagStartIdx { fd.TagString = line[tagStartIdx+1 : tagEndIdx] fd.MysqlTagFieldList = parseTag(reflect.StructTag(fd.TagString).Get("mysql")) line = line[:tagStartIdx] } tokens := strings.Fields(line) if len(tokens) < 2 { continue } fd.FieldName = tokens[0] fd.FieldGoType = tokens[1] sd.Fields = append(sd.Fields, fd) } } return }
func (b *BuildSource) parseField(st *BuildStruct, typeInfo *genbase.TypeInfo, fieldInfo *genbase.FieldInfo, name string) error { field := &BuildField{ parent: st, fieldInfo: fieldInfo, Name: name, } st.Fields = append(st.Fields, field) tag := &BuildTag{ field: field, Name: name, } field.Tag = tag if fieldInfo.Tag != nil { // remove back quote tagBody := fieldInfo.Tag.Value[1 : len(fieldInfo.Tag.Value)-1] structTag := reflect.StructTag(tagBody) searchTag := structTag.Get("search") if searchTag == "-" { tag.Ignore = true } else if idx := strings.Index(searchTag, ","); idx == -1 { // nothing to do } else { for idx != -1 || searchTag != "" { value := searchTag if idx != -1 { value = searchTag[:idx] searchTag = searchTag[idx+1:] } else { searchTag = searchTag[len(value):] } idx = strings.Index(searchTag, ",") switch value { case "id": tag.ID = true case "ngram": tag.Ngram = true case "json": tag.JSON = true case "rank": tag.Rank = true case "string": tag.String = true case "unixtime": tag.UnixTime = true } } } } return nil }
// assignGoTypeToProtoPackage looks for Go and Protobuf types that are referenced by a type in // a package. It will not recurse into protobuf types. func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global typeNameSet, optional map[types.Name]struct{}) { newT, isProto := isFundamentalProtoType(t) if isProto { t = newT } if otherP, ok := global[t.Name]; ok { if _, ok := local[t.Name]; !ok { p.Imports.AddType(&types.Type{ Kind: types.Protobuf, Name: otherP.ProtoTypeName(), }) } return } global[t.Name] = p if _, ok := local[t.Name]; ok { return } // don't recurse into existing proto types if isProto { p.Imports.AddType(t) return } local[t.Name] = p for _, m := range t.Members { if namer.IsPrivateGoName(m.Name) { continue } field := &protoField{} tag := reflect.StructTag(m.Tags).Get("protobuf") if tag == "-" { continue } if err := protobufTagToField(tag, field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil { assignGoTypeToProtoPackage(p, field.Type, local, global, optional) continue } assignGoTypeToProtoPackage(p, m.Type, local, global, optional) } // TODO: should methods be walked? if t.Elem != nil { assignGoTypeToProtoPackage(p, t.Elem, local, global, optional) } if t.Key != nil { assignGoTypeToProtoPackage(p, t.Key, local, global, optional) } if t.Underlying != nil { if t.Kind == types.Alias && isOptionalAlias(t) { optional[t.Name] = struct{}{} } assignGoTypeToProtoPackage(p, t.Underlying, local, global, optional) } }
// Write encoded value into output stream. func Write(out io.Writer, value interface{}) error { valueOf := reflect.ValueOf(value) typeOf := valueOf.Type() p, err := compile(typeOf, reflect.StructTag("")) if err != nil { return err } return p.WriteValue(out, valueOf) }
func structProperties(structType *ast.StructType) (props []PropertyDocs, err error) { for _, f := range structType.Fields.List { names := f.Names if names == nil { // Anonymous fields have no name, use the type as the name // TODO: hide the name and make the properties show up in the embedding struct if t, ok := f.Type.(*ast.Ident); ok { names = append(names, t) } } for _, n := range names { var name, typ, tag, text string var innerProps []PropertyDocs if n != nil { name = proptools.PropertyNameForField(n.Name) } if f.Doc != nil { text = f.Doc.Text() } if f.Tag != nil { tag, err = strconv.Unquote(f.Tag.Value) if err != nil { return nil, err } } switch a := f.Type.(type) { case *ast.ArrayType: typ = "list of strings" case *ast.InterfaceType: typ = "interface" case *ast.Ident: typ = a.Name case *ast.StructType: innerProps, err = structProperties(a) if err != nil { return nil, err } default: typ = fmt.Sprintf("%T", f.Type) } props = append(props, PropertyDocs{ Name: name, Type: typ, Tag: reflect.StructTag(tag), Text: text, Properties: innerProps, }) } } return props, nil }
func fieldNameFor(name string, tag string) string { st := reflect.StructTag(tag) yaml := st.Get("yaml") if len(yaml) > 0 { return strings.SplitN(yaml, ",", 2)[0] } json := st.Get("json") if len(json) > 0 { return strings.SplitN(json, ",", 2)[0] } return name }
func (s *ProcessorSuite) TestTags(c *C) { fixtureSrc := ` package fixture import "gopkg.in/src-d/storable.v1" type Foo struct { storable.Document Int int "foo" } ` pkg := s.processFixture(fixtureSrc) c.Assert(pkg.Models[0].Fields[1].Tag, Equals, reflect.StructTag("foo")) }
func isGenProcField(f *ast.Field) bool { if f.Tag == nil { return false } t, err := strconv.Unquote(f.Tag.Value) if err != nil { fmt.Fprintf(os.Stderr, "parsing tag %s: %v", f.Tag.Value, err) os.Exit(1) } procspec := reflect.StructTag(t).Get("gen_proc") if procspec == "" { return false } split := strings.Split(procspec, ",") return split[0] == "gen_server" }
// // // Parse the tags // // func sqlTags(typeName string, fields *ast.FieldList) *SQLInfo { info := SQLInfo{} info.Fields = make(map[string]string) // [memberName]sqlName info.NoUpdate = make(map[string]struct{}) good := false for _, field := range fields.List { if t := field.Tag; t != nil { s := string(t.Value) // the code uses backticks to metaquote, need to strip them whilst evaluating tag := reflect.StructTag(s[1 : len(s)-1]) if sql := tag.Get("sql"); len(sql) > 0 { //fmt.Println("SQL:", sql) if table := tag.Get("table"); len(table) > 0 { info.Table = table } if key := tag.Get("key"); len(key) > 0 { info.KeyName = string(field.Names[0].Name) info.KeyField = sql } else { info.Fields[field.Names[0].Name] = sql } good = true } if audit := tag.Get("audit"); len(audit) > 0 { //fmt.Println("AUDIT:", audit, "N:", string(field.Names[0].Name)) switch { case audit == "user": info.UserField = string(field.Names[0].Name) case audit == "time": info.TimeField = string(field.Names[0].Name) } } if update := tag.Get("update"); len(update) > 0 { if up, err := strconv.ParseBool(update); err == nil && up == false { //if _, err := strconv.ParseBool(update); err == nil { //fmt.Println("NO UPDATE:", field.Names[0].Name) info.NoUpdate[field.Names[0].Name] = struct{}{} } } } } if good { return &info } return nil }
func TestGetMemberNameFromTag(t *testing.T) { tags := map[string]string{ `bson:"membername,omitempty"`: "membername", `bson:",omitempty"`: "", "membername,omitempty,minsize": "membername", "membername": "membername", ",minsize": "", `json:"name" binding:"required" validate:"nonzero"`: "", } for tag, name := range tags { m := getFieldNameFromTag(reflect.StructTag(tag)) if m != name { t.Errorf("wrong membername detected: '%s'", m) } } }
func structProperties(structType *ast.StructType) (props []PropertyDocs, err error) { for _, f := range structType.Fields.List { //fmt.Printf("%T %#v\n", f, f) for _, n := range f.Names { var name, typ, tag, text string var innerProps []PropertyDocs if n != nil { name = proptools.PropertyNameForField(n.Name) } if f.Doc != nil { text = f.Doc.Text() } if f.Tag != nil { tag, err = strconv.Unquote(f.Tag.Value) if err != nil { return nil, err } } switch a := f.Type.(type) { case *ast.ArrayType: typ = "list of strings" case *ast.InterfaceType: typ = "interface" case *ast.Ident: typ = a.Name case *ast.StructType: innerProps, err = structProperties(a) if err != nil { return nil, err } default: typ = fmt.Sprintf("%T", f.Type) } props = append(props, PropertyDocs{ Name: name, Type: typ, Tag: reflect.StructTag(tag), Text: text, Properties: innerProps, }) } } return props, nil }