func checkDescriptions(objType reflect.Type, seen *map[reflect.Type]bool, t *testing.T) { if _, exists := (*seen)[objType]; exists { return } (*seen)[objType] = true if !strings.Contains(objType.PkgPath(), "openshift/origin") { return } for i := 0; i < objType.NumField(); i++ { structField := objType.FieldByIndex([]int{i}) // these fields don't need descriptions if structField.Name == "TypeMeta" || structField.Name == "ObjectMeta" || structField.Name == "ListMeta" { continue } if structField.Type == reflect.TypeOf(unversioned.Time{}) || structField.Type == reflect.TypeOf(time.Time{}) || structField.Type == reflect.TypeOf(runtime.RawExtension{}) { continue } descriptionTag := structField.Tag.Get("description") if len(descriptionTag) == 0 { t.Errorf("%v", structField.Tag) t.Errorf("%v.%v does not have a description", objType, structField.Name) } switch structField.Type.Kind() { case reflect.Struct: checkDescriptions(structField.Type, seen, t) } } }
func checkJsonTags(objType reflect.Type, seen *map[reflect.Type]bool, t *testing.T) { if _, exists := (*seen)[objType]; exists { return } (*seen)[objType] = true if !strings.Contains(objType.PkgPath(), "openshift/origin") { return } if internalTypesWithAllowedJsonTags.Has(objType.Name()) { return } for i := 0; i < objType.NumField(); i++ { structField := objType.FieldByIndex([]int{i}) jsonTag := structField.Tag.Get("json") if len(jsonTag) != 0 { t.Errorf("%v.%v should not have a json tag", objType, structField.Name) } switch structField.Type.Kind() { case reflect.Struct: checkJsonTags(structField.Type, seen, t) } } }
// recrusively describe a struct's field using our custom struct tags. // This is recursive to allow embedding func extractParams(T reflect.Type) (ret []ParamInfo) { ret = make([]ParamInfo, 0, T.NumField()) for i := 0; i < T.NumField(); i++ { field := T.FieldByIndex([]int{i}) if field.Name == "_" { continue } // a struct means this is an embedded request object if field.Type.Kind() == reflect.Struct { ret = append(extractParams(field.Type), ret...) } else { ret = append(ret, newParamInfo(field)) } } return }
func checkExternalJsonTags(objType reflect.Type, seen *map[reflect.Type]bool, t *testing.T) { if _, exists := (*seen)[objType]; exists { return } (*seen)[objType] = true if !strings.Contains(objType.PkgPath(), "github.com/openshift/origin/pkg") { return } for i := 0; i < objType.NumField(); i++ { structField := objType.FieldByIndex([]int{i}) jsonTag := structField.Tag.Get("json") if len(jsonTag) == 0 { t.Errorf("%v.%v should have a json tag", objType, structField.Name) } switch structField.Type.Kind() { case reflect.Struct: checkExternalJsonTags(structField.Type, seen, t) case reflect.Ptr: checkExternalJsonTags(structField.Type.Elem(), seen, t) } } }
func (m *structCache) getFields(typ reflect.Type) fields { numField := typ.NumField() fs := make(fields, numField) for i := 0; i < numField; i++ { f := typ.Field(i) if f.PkgPath != "" { continue } name, opts := parseTag(f.Tag.Get("msgpack")) if name == "-" { continue } if name == "" { name = f.Name } fieldTyp := typ.FieldByIndex(f.Index).Type fs[name] = &field{ index: f.Index, omitEmpty: opts.Contains("omitempty"), encoder: m.getEncoder(fieldTyp), decoder: m.getDecoder(fieldTyp), } } return fs }
func (m *structCache) newStructField(typ reflect.Type, f *reflect.StructField) field { tokens := strings.Split(f.Tag.Get("msgpack"), ",") name := tokens[0] if name == "-" { return nil } else if name == "" { name = f.Name } baseField := &baseField{ idx: f.Index, name: name, } ft := typ.FieldByIndex(f.Index).Type if encodeFunc, ok := typEncMap[ft]; ok { decodeFunc := typDecMap[ft] return &customField{ encode: encodeFunc, decode: decodeFunc, baseField: baseField, } } switch ft.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return &intField{ baseField: baseField, } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return &uintField{ baseField: baseField, } case reflect.Bool: return &boolField{ baseField: baseField, } case reflect.Float32: return &float32Field{ baseField: baseField, } case reflect.Float64: return &float64Field{ baseField: baseField, } case reflect.Array, reflect.Slice: if ft.Elem().Kind() == reflect.Uint8 { return &bytesField{ baseField: baseField, } } case reflect.String: return &stringField{ baseField: baseField, } } return baseField }
// NewRequestInfo Builds a requestInfo from a requestHandler struct using reflection func NewRequestInfo(T reflect.Type, pth string, description string, returnValue interface{}) (RequestInfo, error) { if T.Kind() == reflect.Ptr { T = T.Elem() } // we only allow funcs and structs if T.Kind() != reflect.Struct && T.Kind() != reflect.Func { return RequestInfo{}, fmt.Errorf("Could not extract request info from non struct type") } ret := RequestInfo{Path: pth, Description: description, Params: make([]ParamInfo, 0), Returns: returnValue, } // take the first part of the path and make it the "group" name for swagger tagging if parts := strings.SplitN(strings.TrimLeft(pth, "/"), "/", 2); len(parts) > 0 { ret.Group = parts[0] } // for funcs we don't create struct based info if T.Kind() != reflect.Struct { return ret, nil } for i := 0; i < T.NumField(); i++ { field := T.FieldByIndex([]int{i}) if field.Name == "_" { continue } // a struct means this is an embedded request object if field.Type.Kind() == reflect.Struct && field.Anonymous { ret.Params = append(extractParams(field.Type), ret.Params...) } else { ret.Params = append(ret.Params, newParamInfo(field)) } } return ret, nil }
func (e *Encoder) _compileStruct(f *encoder, t reflect.Type) { fields := enumStructFields(t) se := &structEncoder{ fields: make([]structEncoderField, len(fields)), } for i, f := range fields { se.fields[i] = structEncoderField{ name: encodeString(nil, reflect.ValueOf(f.Name)), index: f.Index, } e._compile(&se.fields[i].encode, t.FieldByIndex(f.Index).Type) } *f = se.encode }
// enumStructFields enumerates all fields with "binn" tags in a struct type, // including fields of embedded structs. func enumStructFields(t reflect.Type) []reflect.StructField { // We use this queue to visit every field in the struct (both immediate // ones and those in embedded structs), breadth-first. queue := make([][]int, t.NumField()) for i := 0; i < t.NumField(); i++ { queue[i] = []int{i} } var names = make(map[string]bool) var fields []reflect.StructField // Work through the queue. for ; len(queue) > 0; queue = queue[1:] { index := queue[0] field := t.FieldByIndex(index) // todo: Distinguish between empty struct tags and ones that are // simply missing. name := field.Tag.Get("binn") // Visit the fields any embedded structs. if field.Anonymous && field.Type.Kind() == reflect.Struct && name == "" { index = index[:len(index):len(index)] for j := 0; j < field.Type.NumField(); j++ { queue = append(queue, append(index, field.Type.Field(j).Index...)) } continue } // Ignore unexported fields and fields without a "binn" tag. if field.PkgPath != "" && name == "" { continue } field.Name = name field.Index = index fields = append(fields, field) names[name] = true } // Order the fields by their position in the root struct. sort.Sort(fieldsByIndex(fields)) return fields }
// addFieldInfo adds finfo to tinfo.fields if there are no // conflicts, or if conflicts arise from previous fields that were // obtained from deeper embedded structures than finfo. In the latter // case, the conflicting entries are dropped. // A conflict occurs when the path (parent + name) to a field is // itself a prefix of another path, or when two paths match exactly. // It is okay for field paths to share a common, shorter prefix. func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error { var conflicts []int Loop: // First, figure all conflicts. Most working code will have none. for i := range tinfo.fields { oldf := &tinfo.fields[i] if oldf.flags&fMode != newf.flags&fMode { continue } if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns { continue } minl := min(len(newf.parents), len(oldf.parents)) for p := 0; p < minl; p++ { if oldf.parents[p] != newf.parents[p] { continue Loop } } if len(oldf.parents) > len(newf.parents) { if oldf.parents[len(newf.parents)] == newf.name { conflicts = append(conflicts, i) } } else if len(oldf.parents) < len(newf.parents) { if newf.parents[len(oldf.parents)] == oldf.name { conflicts = append(conflicts, i) } } else { if newf.name == oldf.name { conflicts = append(conflicts, i) } } } // Without conflicts, add the new field and return. if conflicts == nil { tinfo.fields = append(tinfo.fields, *newf) return nil } // If any conflict is shallower, ignore the new field. // This matches the Go field resolution on embedding. for _, i := range conflicts { if len(tinfo.fields[i].idx) < len(newf.idx) { return nil } } // Otherwise, if any of them is at the same depth level, it's an error. for _, i := range conflicts { oldf := &tinfo.fields[i] if len(oldf.idx) == len(newf.idx) { f1 := typ.FieldByIndex(oldf.idx) f2 := typ.FieldByIndex(newf.idx) return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")} } } // Otherwise, the new field is shallower, and thus takes precedence, // so drop the conflicting fields from tinfo and append the new one. for c := len(conflicts) - 1; c >= 0; c-- { i := conflicts[c] copy(tinfo.fields[i:], tinfo.fields[i+1:]) tinfo.fields = tinfo.fields[:len(tinfo.fields)-1] } tinfo.fields = append(tinfo.fields, *newf) return nil }
func typeFieldByLoc(t reflect.Type, loc fieldLoc) reflect.StructField { if len(loc.path) > 0 { return t.FieldByIndex(loc.path) } return t.Field(loc.i) }