package promptkit import ( "encoding/json" "reflect" "strconv" ) type providerVisit struct { typ reflect.Type ptr uintptr } type invalidProviderNumber struct{} // providerMapForMapping converts JSON-shaped Go containers to ConfigKit's // built-in mapping containers. Unsupported values remain for Mapping to report // as deferred source diagnostics during construction. func providerMapForMapping(values map[string]any) map[string]any { if values == nil { return nil } converted := make(map[string]any, len(values)) active := make(map[providerVisit]bool) for key, value := range values { converted[key] = providerValueForMapping(reflect.ValueOf(value), active, 0) } return converted } func providerValueForMapping(value reflect.Value, active map[providerVisit]bool, depth int) any { if !value.IsValid() { return nil } if depth > 100 { return value.Interface() } for value.Kind() == reflect.Interface || value.Kind() == reflect.Pointer { if value.IsNil() { return nil } if value.Kind() == reflect.Pointer { visit := providerVisit{typ: value.Type(), ptr: value.Pointer()} if active[visit] { return value.Interface() } active[visit] = true defer delete(active, visit) } value = value.Elem() } if value.CanInterface() { if number, ok := value.Interface().(json.Number); ok { if _, err := json.Marshal(number); err != nil { return invalidProviderNumber{} } if integer, err := strconv.ParseInt(string(number), 10, 64); err == nil { return integer } if integer, err := strconv.ParseUint(string(number), 10, 64); err == nil { return integer } if decimal, err := strconv.ParseFloat(string(number), 64); err == nil { return decimal } return number } } switch value.Kind() { case reflect.Map: if value.IsNil() { return nil } if value.Type().Key().Kind() != reflect.String { return value.Interface() } visit := providerVisit{typ: value.Type(), ptr: value.Pointer()} if active[visit] { return value.Interface() } active[visit] = true defer delete(active, visit) converted := make(map[string]any, value.Len()) iter := value.MapRange() for iter.Next() { converted[iter.Key().String()] = providerValueForMapping(iter.Value(), active, depth+1) } return converted case reflect.Slice, reflect.Array: if value.Kind() == reflect.Slice { if value.IsNil() { return nil } visit := providerVisit{typ: value.Type(), ptr: value.Pointer()} if visit.ptr != 0 { if active[visit] { return value.Interface() } active[visit] = true defer delete(active, visit) } } converted := make([]any, value.Len()) for index := range converted { converted[index] = providerValueForMapping(value.Index(index), active, depth+1) } return converted case reflect.String: return value.String() case reflect.Bool: return value.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return value.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return value.Uint() case reflect.Float32, reflect.Float64: return value.Float() default: return value.Interface() } }