// Licensed to Datastore, Inc. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Datastore, Inc. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // This file is a copy of the original file from the datastore-go package. // The original file is located at: // https://github.com/ClickHouse/datastore-go/blob/226a902d120aa46e3883fbf6a5a2667dfb9e90d2/struct_map.go package mock import ( "fmt" "reflect" "sync" ) type structMap struct { cache sync.Map } func (m *structMap) Map(op string, columns []string, s any, ptr bool) ([]any, error) { v := reflect.ValueOf(s) if v.Kind() != reflect.Ptr { return nil, &OpError{ Op: op, Err: fmt.Errorf("must pass a pointer, not a value, to %s destination", op), } } if v.IsNil() { return nil, &OpError{ Op: op, Err: fmt.Errorf("nil pointer passed to %s destination", op), } } t := reflect.TypeOf(s) if v = reflect.Indirect(v); t.Kind() == reflect.Ptr { t = t.Elem() } if v.Kind() != reflect.Struct { return nil, &OpError{ Op: op, Err: fmt.Errorf("%s expects a struct dest", op), } } var ( index map[string][]int values = make([]any, 0, len(columns)) ) switch idx, found := m.cache.Load(t); { case found: index = idx.(map[string][]int) default: index = structIdx(t) m.cache.Store(t, index) } for _, name := range columns { idx, found := index[name] if !found { return nil, &OpError{ Op: op, Err: fmt.Errorf("missing destination name %q in %T", name, s), } } switch field := v.FieldByIndex(idx); { case ptr: values = append(values, field.Addr().Interface()) default: values = append(values, field.Interface()) } } return values, nil } func structIdx(t reflect.Type) map[string][]int { fields := make(map[string][]int) for i := 0; i < t.NumField(); i++ { var ( f = t.Field(i) name = f.Name ) if tn := f.Tag.Get("ch"); len(tn) != 0 { name = tn } switch { case name == "-", len(f.PkgPath) != 0 && !f.Anonymous: continue } switch { case f.Anonymous: if f.Type.Kind() != reflect.Ptr { for k, idx := range structIdx(f.Type) { fields[k] = append(f.Index, idx...) } } default: fields[name] = f.Index } } return fields } func newStructMap() *structMap { return &structMap{} }