package form import ( "fmt" "reflect" "time" "github.com/photoprism/photoprism/pkg/clean" ) // Report returns form fields as table rows for reports. func Report(f interface{}) (rows [][]string, cols []string) { cols = []string{"Filter", "Type", "Examples", "Notes"} v := reflect.ValueOf(f) if v.Kind() == reflect.Ptr { v = v.Elem() } if v.Kind() != reflect.Struct { return rows, cols } rows = make([][]string, 0, v.NumField()) // Iterate through all form fields. for i := 0; i < v.NumField(); i++ { // Skip unexported fields. if !v.Type().Field(i).IsExported() { continue } // Get info from struct field tags. fieldValue := v.Field(i) fieldName := v.Type().Field(i).Tag.Get("form") fieldInfo := v.Type().Field(i).Tag.Get("serialize") notes := v.Type().Field(i).Tag.Get("notes") // Serialize field values as string. if fieldName != "" && fieldName != "q" && fieldInfo != "-" && notes != "-" { example := v.Type().Field(i).Tag.Get("example") typeName := "any" switch t := fieldValue.Interface().(type) { case time.Time: typeName = "timestamp" if example == "" { example = fmt.Sprintf("%s:\"2022-01-30\"", fieldName) } case int, int8, int16, int32, int64: typeName = "number" if example == "" { example = fmt.Sprintf("%s:0 %s:3", fieldName, fieldName) } case uint, uint8, uint16, uint32, uint64: typeName = "number" if example == "" { example = fmt.Sprintf("%s:-1 %s:2", fieldName, fieldName) } case float32, float64: typeName = "decimal" if example == "" { example = fmt.Sprintf("%s:1.245", fieldName) } case string: typeName = "string" if example == "" { example = fmt.Sprintf("%s:\"name\"", fieldName) } case bool: typeName = "switch" if example == "" { example = fmt.Sprintf("%s:yes", fieldName) } default: log.Warnf("failed reporting on %T %s", t, clean.Token(fieldName)) continue } rows = append(rows, []string{fieldName, typeName, example, notes}) } } return rows, cols }