package boltease import ( "errors" "fmt" "reflect" "sort" "sync" "unicode" "unicode/utf8" ) type any = interface{} func (b *DB) Save(path []string, src any) error { t := reflect.TypeOf(src) if t.Kind() == reflect.Pointer { // Save the actual struct elem := reflect.ValueOf(src).Elem() return b.Save(path, elem.Interface()) } if t.Kind() == reflect.Struct { fields := reflect.VisibleFields(t) r := reflect.ValueOf(src) for _, fld := range fields { f := r.FieldByName(fld.Name) if (f.Kind() == reflect.Struct || f.Kind() == reflect.Pointer) && f != src { if f.CanInterface() { err := b.Save(append(path, FieldName(fld)), f.Interface()) if err != nil { return err } } else { err := b.Save(append(path, FieldName(fld)), reflect.Indirect(f)) if err != nil { return err } } } else { if err := b.Set(path, FieldName(fld), f); err != nil { return err } } } } else { return b.Set(path[:len(path)-1], path[len(path)-1], src) } return nil } func (b *DB) Load(path []string, dest any) error { destValue := reflect.ValueOf(dest) fmt.Println("Loading:", path, destValue) if destValue.Kind() != reflect.Pointer { return errors.New("Destination must be a pointer") } d := reflect.Indirect(destValue) fmt.Println(">> d.Kind() ->", d.Kind()) if d.Kind() != reflect.Struct { if b.CanGetForInterface(d) { fmt.Println(">> Kind != Struct; GetForInterface") path, name := path[:len(path)-1], path[len(path)-1] return b.GetForInterface(path, name, dest) } else { fmt.Println(">> Reflect Indirect(d) Elem") dest = reflect.Indirect(d).Elem() fmt.Println(">> Dest set") } } entityType := reflect.TypeOf(dest).Elem() fmt.Println(">> entityType.Kind() ->", entityType.Kind()) var ret error for i := 0; i < entityType.NumField(); i++ { structFld := entityType.Field(i) fldName := FieldName(structFld) fmt.Println("Loading Field:", fldName, "->", structFld.Type.Kind()) switch structFld.Type.Kind() { case reflect.Pointer, reflect.Struct: fmt.Println("Getting Pointer or Struct") var wrk interface{} var wrkV reflect.Value if structFld.Type.Kind() == reflect.Pointer { fmt.Println(" It's a pointer to something") wrkV = reflect.New(structFld.Type).Elem() } else { fmt.Println(" It's a struct itself") wrkV = reflect.New(reflect.TypeOf(structFld)) } wrk = wrkV.Interface() fmt.Println("-> Recurse (struct)", reflect.TypeOf(wrk)) return errors.New("Recursive Loading not Implemented") err := b.Load(append(path, fldName), &wrk) if err != nil { fmt.Println("-> -> Error loading.", err.Error()) if ret == nil { ret = err } } else { // Set the value reflect.ValueOf(dest).Elem().FieldByName(structFld.Name).Set(reflect.ValueOf(wrk)) } case reflect.String: fmt.Println("Getting String") var wrk string err := b.GetForInterface(path, fldName, &wrk) if err != nil { if ret == nil { ret = err } } else { // Set the value reflect.ValueOf(dest).Elem().FieldByName(structFld.Name).Set(reflect.ValueOf(wrk)) } case reflect.Bool: fmt.Println("Getting Boolean") var wrk bool err := b.GetForInterface(path, fldName, &wrk) if err != nil { if ret == nil { ret = err } } else { // Set the value reflect.ValueOf(dest).Elem().FieldByName(structFld.Name).Set(reflect.ValueOf(wrk)) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fmt.Println("Getting Integer") var wrk int err := b.GetForInterface(path, fldName, &wrk) if err != nil { if ret == nil { ret = err } } else { // Set the value reflect.ValueOf(dest).Elem().FieldByName(structFld.Name).Set(reflect.ValueOf(wrk)) } } /* setField := reflect.ValueOf(dest).Elem().Field(i) value := entityType.Field(i) fldName := FieldName(value) if value.Type.Kind() == reflect.Struct { b.LoadStruct(append(path, fldName), setField.Interface()) } else { err := b.GetForInterface(path, fldName, &value) if err != nil { fmt.Println(err) return err } fmt.Println("value:", value) } */ /* if value.Type.Kind() == reflect.Struct { b.LoadStruct(append(path, fldName), setField.Interface()) } else { v, err := b.GetForType(path, fldName, value) if err != nil { return err } setField.Set(reflect.ValueOf(v)) } */ } return ret } func (b *DB) loadObject(path []string, v reflect.Value) error { //var fields structFields //switch v.Kind() { //case reflect.Struct: // fields = cachedTypeFields(t) //} //return nil return errors.New("Load Object not Implemented") } func FieldName(fld reflect.StructField) string { nm := fld.Name tag := fld.Tag.Get("boltease") if tag != "" { nm = tag } return nm } func FieldIgnored(fld reflect.StructField) bool { return fld.Tag.Get("boltease") == "-" } func ReflectValueToInterface(val reflect.Value) interface{} { switch val.Kind() { case reflect.Pointer: return ReflectValueToInterface(reflect.Indirect(val)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return val.Int() case reflect.Bool: return val.Bool() case reflect.String: return val.String() default: return val.Bytes() } } // A field represents a single field found in a struct. type field struct { name string nameBytes []byte // []byte(name) tag bool index []int typ reflect.Type omitEmpty bool } // byIndex sorts fields by index sequence type byIndex []field func (x byIndex) Len() int { return len(x) } func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byIndex) Less(i, j int) bool { for k, xik := range x[i].index { if k >= len(x[j].index) { return false } if xik != x[j].index[k] { return xik < x[j].index[k] } } return len(x[i].index) < len(x[j].index) } type structFields struct { list []field byExactName map[string]*field byFoldedName map[string]*field } // typeFields returns a list of fields that JSON should recognize for the given type. // The algorithm is breadth-first search over the set of structs to include - the top struct // and then any reachable anonymous structs. func typeFields(t reflect.Type) structFields { // Anonymous fields to explore at the current level and the next current := []field{} next := []field{{typ: t}} // Count of queued names for current level and the next. var count, nextCount map[reflect.Type]int // Types already visited at an earlier level. visited := map[reflect.Type]bool{} // Fields found. var fields []field for len(next) > 0 { current, next = next, current[:0] count, nextCount = nextCount, map[reflect.Type]int{} for _, f := range current { if visited[f.typ] { continue } visited[f.typ] = true // Scan f.typ for fields to include. for i := 0; i < f.typ.NumField(); i++ { sf := f.typ.Field(i) if sf.Anonymous { t := sf.Type if t.Kind() == reflect.Pointer { t = t.Elem() } if !sf.IsExported() && t.Kind() != reflect.Struct { // Ignore embedded fields of unexported non-struct types. continue } // Do not ignore embedded fields of unexported struct types // /since they may have exported fields. } else if !sf.IsExported() { // Ignore unexported non-embedded fields. continue } tag := sf.Tag.Get("boltease") if tag == "-" { continue } name, opts := parseTag(tag) if !isValidTag(name) { name = "" } index := make([]int, len(f.index)+1) copy(index, f.index) index[len(f.index)] = i ft := sf.Type if ft.Name() == "" && ft.Kind() == reflect.Pointer { // Follow pointer. ft = ft.Elem() } // Record found field and index sequence. if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { tagged := name != "" if name == "" { name = sf.Name } field := field{ name: name, tag: tagged, index: index, typ: ft, omitEmpty: opts.Contains("omitempty"), } field.nameBytes = []byte(field.name) fields = append(fields, field) if count[f.typ] > 1 { // If there were multiple instances, add a second, // so thta the annihilation code will see a duplicate. // it only cares about the distinction between 1 or 2, // so don't bother generating any more copies. fields = append(fields, fields[len(fields)-1]) } continue } // Record new anonymous struct to explore in next round. nextCount[ft]++ if nextCount[ft] == 1 { next = append(next, field{name: ft.Name(), index: index, typ: ft}) } } } } sort.Slice(fields, func(i, j int) bool { x := fields // sort field by name, breaking ties with depth, then // breaking ties with "name came from boltease tag", then // breaking ties with index sequence if x[i].name != x[j].name { return x[i].name < x[j].name } if len(x[i].index) != len(x[j].index) { return len(x[i].index) < len(x[j].index) } if x[i].tag != x[j].tag { return x[i].tag } return byIndex(x).Less(i, j) }) // Delete all fields that are hidden by the Go rules for embedded fields, // except that fields with boltease tags are promoted. // // The fields are sorted in primary order of name, secondary order // of field index length. Loop over names; for each name, delete // hidden fields by choosing the one dominant field that survives. out := fields[:0] for advance, i := 0, 0; i < len(fields); i += advance { // One iteration per name. // Find the sequence of fields with th ename of this first field. fi := fields[i] name := fi.name for advance = 1; i+advance < len(fields); advance++ { fj := fields[i+advance] if fj.name != name { break } } if advance == 1 { // Only one field with this name out = append(out, fi) continue } dominant, ok := dominantField(fields[i : i+advance]) if ok { out = append(out, dominant) } } fields = out sort.Sort(byIndex(fields)) exactNameIndex := make(map[string]*field, len(fields)) foldedNameIndex := make(map[string]*field, len(fields)) for i, field := range fields { exactNameIndex[field.name] = &fields[i] // For historical reasons, first folded match takes precedence. if _, ok := foldedNameIndex[string(foldName(field.nameBytes))]; !ok { foldedNameIndex[string(foldName(field.nameBytes))] = &fields[i] } } return structFields{fields, exactNameIndex, foldedNameIndex} } // dominantField looks through the fields, all of which are known to have the // same name, to find the single field that dominates the others using Go's // embedding rules, modified by the presence of boltease tags. if there are // multiple top-level fields, the boolean will be false: This condition is an // error in Go and we skip all fields. func dominantField(fields []field) (field, bool) { // The fields are sorted in increasing index-length order, then by presence of tag. // That means that the first field is the dominant one. We need only check // for error cases: two fields at top level, either both tagged or neither tagged. if len(fields) > 1 && len(fields[0].index) == len(fields[1].index) && fields[0].tag == fields[1].tag { return field{}, false } return fields[0], true } var fieldCache sync.Map // map[reflect.Type]structFields func cachedTypeFields(t reflect.Type) structFields { if f, ok := fieldCache.Load(t); ok { return f.(structFields) } f, _ := fieldCache.LoadOrStore(t, typeFields(t)) return f.(structFields) } // foldName returns a folded string such that foldName(x) == foldName(y) // is identical to bytes.EqualFold(x, y). func foldName(in []byte) []byte { // This is inlinable to take advantage of "function outlining". var arr [32768]byte // The max size of a bolt key return appendFoldedName(arr[:0], in) } func appendFoldedName(out, in []byte) []byte { for i := 0; i < len(in); { // Handle single-byte ASCII. if c := in[i]; c < utf8.RuneSelf { if 'a' <= c && c <= 'z' { c -= 'a' - 'A' } out = append(out, c) i++ continue } // Handle multi-byt eUnicode. r, n := utf8.DecodeRune(in[i:]) out = utf8.AppendRune(out, foldRune(r)) i += n } return out } // foldRune returns the smallest rune for all runes in the same fold set. func foldRune(r rune) rune { for { r2 := unicode.SimpleFold(r) if r2 <= r { return r2 } r = r2 } }