func ExampleGetFieldTag() { s := MyStruct{} tag, err := reflections.GetFieldTag(s, "FirstField", "matched") if err != nil { log.Fatal(err) } fmt.Println(tag) tag, err = reflections.GetFieldTag(s, "ThirdField", "unmatched") if err != nil { log.Fatal(err) } fmt.Println(tag) }
// ProcessCopy processes copy. func (dc *DeepCopier) ProcessCopy() error { fields, _ := reflections.Fields(dc.Tagged) for _, field := range fields { fieldOptions := &FieldOptions{ SourceField: field, DestinationField: field, WithContext: false, Skip: false, } tagOptions, _ := reflections.GetFieldTag(dc.Tagged, field, TagName) if tagOptions != "" { opts := dc.GetTagOptions(tagOptions) if _, ok := opts[FieldOptionName]; ok { fieldName := opts[FieldOptionName] if !dc.Reversed { fieldOptions.SourceField = fieldName } else { fieldOptions.DestinationField = fieldName } } if _, ok := opts[ContextOptionName]; ok { fieldOptions.WithContext = true } if _, ok := opts[SkipOptionName]; ok { fieldOptions.Skip = true } } if err := dc.SetField(fieldOptions); err != nil { return err } } return nil }
func (t *MyTools) GetValueByNamespace(object interface{}, ns []string) interface{} { // current level of namespace current := ns[0] fields, err := reflections.Fields(object) if err != nil { fmt.Printf("Could not return fields for object{%v}\n", object) return nil } for _, field := range fields { tag, err := reflections.GetFieldTag(object, field, "json") if err != nil { fmt.Printf("Could not find tag for field{%s}\n", field) return nil } // remove omitempty from tag tag = strings.Replace(tag, ",omitempty", "", -1) if tag == current { val, _ := reflections.GetField(object, field) // handling of special cases for slice and map switch reflect.TypeOf(val).Kind() { case reflect.Slice: idx, _ := strconv.Atoi(ns[1]) val := reflect.ValueOf(val) if val.Index(idx).Kind() == reflect.Struct { return t.GetValueByNamespace(val.Index(idx).Interface(), ns[2:]) } else { return val.Index(idx).Interface() } case reflect.Map: key := ns[1] // try uint64 map (memory_stats case) if vi, ok := val.(map[string]uint64); ok { return vi[key] } // try with hugetlb map (hugetlb_stats case) val := reflect.ValueOf(val) kval := reflect.ValueOf(key) if reflect.TypeOf(val.MapIndex(kval).Interface()).Kind() == reflect.Struct { return t.GetValueByNamespace(val.MapIndex(kval).Interface(), ns[2:]) } default: // last ns, return value found if len(ns) == 1 { return val } else { // or go deeper return t.GetValueByNamespace(val, ns[1:]) } } } } return nil }
// ProcessCopy processes copy. func (dc *DeepCopier) ProcessCopy() error { fields := []string{} val := reflect.ValueOf(dc.Tagged).Elem() for i := 0; i < val.NumField(); i++ { typ := val.Type().Field(i) // Embedded struct if typ.Anonymous { f, _ := reflections.Fields(val.Field(i).Interface()) fields = append(fields, f...) } else { fields = append(fields, typ.Name) } } for _, field := range fields { fieldOptions := &FieldOptions{ SourceField: field, DestinationField: field, WithContext: false, Skip: false, } tagOptions, _ := reflections.GetFieldTag(dc.Tagged, field, TagName) if tagOptions != "" { opts := dc.GetTagOptions(tagOptions) if _, ok := opts[FieldOptionName]; ok { fieldName := opts[FieldOptionName] if !dc.Reversed { fieldOptions.SourceField = fieldName } else { fieldOptions.DestinationField = fieldName } } if _, ok := opts[ContextOptionName]; ok { fieldOptions.WithContext = true } if _, ok := opts[SkipOptionName]; ok { fieldOptions.Skip = true } } if err := dc.SetField(fieldOptions); err != nil { return err } } return nil }
// createFieldInfos creates the fieldInfos for a struct s. // Only information from the struct (headerName, fieldName and kind) is available, // all field positions are initialized with an invalid value of -1 func createFieldInfos(s interface{}) (fieldInfos, error) { if reflect.TypeOf(s).Kind() != reflect.Struct { return nil, ErrNoStruct } fieldInfos := []fieldInfo{} headerNameMap := map[string]interface{}{} // to detect duplicate csv tag names fieldNames, err := reflections.Fields(s) if err != nil { return nil, err } for _, fieldName := range fieldNames { headerName, err := reflections.GetFieldTag(s, fieldName, "csv") if err != nil { return nil, err } // csv fieldtags that contain a dash are ignored if strings.Contains(headerName, "-") { continue } if _, ok := headerNameMap[headerName]; ok { return nil, fmt.Errorf("duplicate csv tag name: %s", headerName) } headerNameMap[headerName] = nil kind, err := reflections.GetFieldKind(s, fieldName) if err != nil { return nil, err } if len(headerName) == 0 { return nil, fmt.Errorf("empty csv tag for field: %s", fieldName) } fieldInfos = append(fieldInfos, fieldInfo{ headerName: headerName, fieldName: fieldName, position: -1, kind: kind, }) } return fieldInfos, nil }
func formTagFromField(fieldName string, target interface{}) (string, error) { fieldKind, err := reflections.GetFieldKind(target, fieldName) f, _ := reflections.GetField(target, fieldName) fmt.Printf("Field %v\n", f) if err != nil { return "", err } choices, _ := reflections.GetFieldTag(target, fieldName, "choices") switch fieldKind { case reflect.String: if choices != "" { return "list", nil } return "input", nil case reflect.Slice: return "checkbox", nil case reflect.Bool: return "confirm", nil } return "", nil }
// Reflects on the values in the commandLineOptions structure, and fills it // with values either from the command line, or from the ENV. func parseArgs(opts *commandLineOptions, args []string) { // Create a flag set for args flags := flag.NewFlagSet(commandLineName, flag.ExitOnError) // Get the fields for the strucutre fields, _ := reflections.Fields(opts) // Loop through each field of the structure, and define a flag for it for i := 0; i < len(fields); i++ { fieldName := fields[i] flagName, _ := reflections.GetFieldTag(opts, fieldName, "flag") fieldKind, _ := reflections.GetFieldKind(opts, fieldName) if fieldKind == reflect.String { flags.String(flagName, "", "") } else if fieldKind == reflect.Bool { flags.Bool(flagName, false, "") } else { exitAndError(fmt.Sprintf("Could not create flag for %s", fieldName)) } } // Define our custom usage text flags.Usage = func() { fmt.Printf("%s\n", commandLineUsage) } // Search the args until we find a "--", which signifies the user has started // defining options. var argumentFlags []string started := false for i := 0; i < len(args); i++ { if strings.HasPrefix(args[i], "--") { started = true } if started { argumentFlags = append(argumentFlags, args[i]) } } // Now parse the flags flags.Parse(argumentFlags) // Now the flag set has been populated with values, retrieve them and // set them (or use the ENV variable if empty) for i := 0; i < len(fields); i++ { fieldName := fields[i] fieldKind, _ := reflections.GetFieldKind(opts, fieldName) // Grab the flags we need flagName, _ := reflections.GetFieldTag(opts, fieldName, "flag") envName, _ := reflections.GetFieldTag(opts, fieldName, "env") required, _ := reflections.GetFieldTag(opts, fieldName, "required") // Grab the value from the flag value := flags.Lookup(flagName).Value.String() // If the value of the flag is empty, try the ENV if value == "" { value = os.Getenv(envName) } // Do validation if required == "true" && value == "" { exitAndError(fmt.Sprintf("missing %s", flagName)) } // Set the value in the right way if fieldKind == reflect.String { reflections.SetField(opts, fieldName, value) } else if fieldKind == reflect.Bool { // The bool is converted to a string above if value == "true" { reflections.SetField(opts, fieldName, true) } else { reflections.SetField(opts, fieldName, false) } } else { exitAndError(fmt.Sprintf("Could not set value of %s", fieldName)) } } }
// Loads the config from the CLI and config files that are present. func (l *Loader) Load() error { // Try and find a config file, either passed in the command line using // --config, or in one of the default configuration file paths. if l.CLI.String("config") != "" { file := File{Path: l.CLI.String("config")} // Because this file was passed in manually, we should throw an error // if it doesn't exist. if file.Exists() { l.File = &file } else { return fmt.Errorf("A configuration file could not be found at: %s", file.AbsolutePath()) } } else if len(l.DefaultConfigFilePaths) > 0 { for _, path := range l.DefaultConfigFilePaths { file := File{Path: path} // If the config file exists, save it to the loader and // don't bother checking the others. if file.Exists() { l.File = &file break } } } // If a file was found, then we should load it if l.File != nil { // Attempt to load the config file we've found if err := l.File.Load(); err != nil { return err } } // Now it's onto actually setting the fields. We start by getting all // the fields from the configuration interface var fields []string fields, _ = reflections.Fields(l.Config) // Loop through each of the fields, and look for tags and handle them // appropriately for _, fieldName := range fields { // Start by loading the value from the CLI context if the tag // exists cliName, _ := reflections.GetFieldTag(l.Config, fieldName, "cli") if cliName != "" { // Load the value from the CLI Context err := l.setFieldValueFromCLI(fieldName, cliName) if err != nil { return err } } // Are there any normalizations we need to make? normalization, _ := reflections.GetFieldTag(l.Config, fieldName, "normalize") if normalization != "" { // Apply the normalization err := l.normalizeField(fieldName, normalization) if err != nil { return err } } // Check for field deprecation deprecationError, _ := reflections.GetFieldTag(l.Config, fieldName, "deprecated") if deprecationError != "" { // If the deprecated field's value isn't emtpy, then we // return the deprecation error message. if !l.fieldValueIsEmpty(fieldName) { return fmt.Errorf(deprecationError) } } // Perform validations validationRules, _ := reflections.GetFieldTag(l.Config, fieldName, "validate") if validationRules != "" { // Determine the label for the field label, _ := reflections.GetFieldTag(l.Config, fieldName, "label") if label == "" { // Use the cli name if it exists, but if it // doesn't, just default to the structs field // name. Not great, but works! if cliName != "" { label = cliName } else { label = fieldName } } // Validate the fieid, and if it fails, return it's // error. err := l.validateField(fieldName, label, validationRules) if err != nil { return err } } } return nil }
func reflectField(fieldName string, target interface{}) (Field, error) { formTag, err := reflections.GetFieldTag(target, fieldName, "form") if err != nil { return nil, err } if formTag == "" { s, e := formTagFromField(fieldName, target) if e != nil || s == "" { return nil, e } formTag = s } if !validFieldType(fieldName, target, formTag) { return nil, fmt.Errorf("target field %s : %s", fieldName, formTag) } var message, choicesString string var choices []string message, err = reflections.GetFieldTag(target, fieldName, "message") if err != nil { return nil, err } if message == "" { message = fieldName } choicesString, err = reflections.GetFieldTag(target, fieldName, "choices") if err != nil { return nil, err } if choicesString != "" { choices = strings.Split(choicesString, ",") for i, c := range choices { choices[i] = strings.Trim(c, " ") } } var field Field switch formTag { case "input": field = &Input{ Name: fieldName, Message: message, } case "confirm": field = &Confirm{ Name: fieldName, Message: message, } case "password": field = &Password{ Name: fieldName, Message: message, } case "list": field = &List{ Name: fieldName, Message: message, Choices: choices, } case "checkbox": field = &Checkbox{ Name: fieldName, Message: message, Choices: choices, } } return field, nil }