mirror of
https://github.com/semaphoreui/semaphore.git
synced 2024-11-23 12:30:41 +01:00
feat(backup): restore backup struct
This commit is contained in:
parent
9aa492b53f
commit
117d16c418
@ -269,292 +269,6 @@ func GetBackup(projectID int, store db.Store) (*BackupFormat, error) {
|
|||||||
return backup.format()
|
return backup.format()
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalValue(v reflect.Value) (interface{}, error) {
|
|
||||||
// Handle pointers
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
if v.IsNil() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return marshalValue(v.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle structs
|
|
||||||
if v.Kind() == reflect.Struct {
|
|
||||||
typeOfV := v.Type()
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
fieldValue := v.Field(i)
|
|
||||||
fieldType := typeOfV.Field(i)
|
|
||||||
|
|
||||||
// Handle anonymous fields (embedded structs)
|
|
||||||
if fieldType.Anonymous {
|
|
||||||
embeddedValue, err := marshalValue(fieldValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if embeddedMap, ok := embeddedValue.(map[string]interface{}); ok {
|
|
||||||
// Merge embedded struct fields into parent result map
|
|
||||||
for k, v := range embeddedMap {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tag := fieldType.Tag.Get("backup")
|
|
||||||
|
|
||||||
// Check if the field should be backed up
|
|
||||||
if tag == "-" {
|
|
||||||
continue // Skip fields with backup:"-"
|
|
||||||
} else if tag == "" {
|
|
||||||
// Get the field name from the "db" tag
|
|
||||||
tag = fieldType.Tag.Get("db")
|
|
||||||
if tag == "" || tag == "-" {
|
|
||||||
continue // Skip if "db" tag is empty or "-"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively process the field value
|
|
||||||
value, err := marshalValue(fieldValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result[tag] = value
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle slices and arrays
|
|
||||||
if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
|
|
||||||
if v.IsNil() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
var result []interface{}
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
elemValue, err := marshalValue(v.Index(i))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result = append(result, elemValue)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle maps
|
|
||||||
if v.Kind() == reflect.Map {
|
|
||||||
if v.IsNil() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
for _, key := range v.MapKeys() {
|
|
||||||
// Assuming the key is a string
|
|
||||||
mapKey := fmt.Sprintf("%v", key.Interface())
|
|
||||||
mapValue, err := marshalValue(v.MapIndex(key))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result[mapKey] = mapValue
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle other types (int, string, etc.)
|
|
||||||
return v.Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalStruct deserializes JSON data into a struct,
|
|
||||||
// using the "db" tag for field names and excluding fields with backup:"-".
|
|
||||||
func UnmarshalStruct(data []byte, v interface{}) error {
|
|
||||||
// Parse the JSON data into an interface{}
|
|
||||||
var jsonData interface{}
|
|
||||||
if err := json.Unmarshal(data, &jsonData); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Start the recursive unmarshaling process
|
|
||||||
return unmarshalValue(jsonData, reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalValue(data interface{}, v reflect.Value) error {
|
|
||||||
// Handle pointers
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
// Initialize pointer if it's nil
|
|
||||||
if v.IsNil() {
|
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
|
||||||
}
|
|
||||||
return unmarshalValue(data, v.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle structs
|
|
||||||
if v.Kind() == reflect.Struct {
|
|
||||||
// Data should be a map
|
|
||||||
m, ok := data.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected object for struct, got %T", data)
|
|
||||||
}
|
|
||||||
return unmarshalStruct(m, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle slices and arrays
|
|
||||||
if v.Kind() == reflect.Slice {
|
|
||||||
// Data should be an array
|
|
||||||
dataSlice, ok := data.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected array for slice, got %T", data)
|
|
||||||
}
|
|
||||||
// Create a new slice
|
|
||||||
slice := reflect.MakeSlice(v.Type(), len(dataSlice), len(dataSlice))
|
|
||||||
for i := 0; i < len(dataSlice); i++ {
|
|
||||||
elem := slice.Index(i)
|
|
||||||
if err := unmarshalValue(dataSlice[i], elem); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v.Set(slice)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle maps
|
|
||||||
if v.Kind() == reflect.Map {
|
|
||||||
// Data should be a map
|
|
||||||
dataMap, ok := data.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected object for map, got %T", data)
|
|
||||||
}
|
|
||||||
mapType := v.Type()
|
|
||||||
mapValue := reflect.MakeMap(mapType)
|
|
||||||
for key, value := range dataMap {
|
|
||||||
keyVal := reflect.ValueOf(key).Convert(mapType.Key())
|
|
||||||
valVal := reflect.New(mapType.Elem()).Elem()
|
|
||||||
if err := unmarshalValue(value, valVal); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mapValue.SetMapIndex(keyVal, valVal)
|
|
||||||
}
|
|
||||||
v.Set(mapValue)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle basic types
|
|
||||||
if err := setBasicType(data, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalStruct(data map[string]interface{}, v reflect.Value) error {
|
|
||||||
t := v.Type()
|
|
||||||
|
|
||||||
// Build a map of db tags to field indices
|
|
||||||
fieldMap := make(map[string]int)
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
fieldType := t.Field(i)
|
|
||||||
|
|
||||||
// Skip fields with backup:"-"
|
|
||||||
if backupTag := fieldType.Tag.Get("backup"); backupTag == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the field name from the "db" tag
|
|
||||||
dbTag := fieldType.Tag.Get("db")
|
|
||||||
if dbTag == "" || dbTag == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldMap[dbTag] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over the JSON data and set struct fields
|
|
||||||
for key, value := range data {
|
|
||||||
if index, ok := fieldMap[key]; ok {
|
|
||||||
field := v.Field(index)
|
|
||||||
if !field.CanSet() {
|
|
||||||
continue // Skip unexportable fields
|
|
||||||
}
|
|
||||||
if err := unmarshalValue(value, field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setBasicType(data interface{}, v reflect.Value) error {
|
|
||||||
if !v.CanSet() {
|
|
||||||
return fmt.Errorf("cannot set value of type %v", v.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
b, ok := data.(bool)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected bool for field, got %T", data)
|
|
||||||
}
|
|
||||||
v.SetBool(b)
|
|
||||||
case reflect.String:
|
|
||||||
s, ok := data.(string)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected string for field, got %T", data)
|
|
||||||
}
|
|
||||||
v.SetString(s)
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
n, ok := toFloat64(data)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected number for field, got %T", data)
|
|
||||||
}
|
|
||||||
v.SetInt(int64(n))
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
n, ok := toFloat64(data)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected number for field, got %T", data)
|
|
||||||
}
|
|
||||||
v.SetUint(uint64(n))
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
n, ok := toFloat64(data)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected number for field, got %T", data)
|
|
||||||
}
|
|
||||||
v.SetFloat(n)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported kind %v", v.Kind())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toFloat64(data interface{}) (float64, bool) {
|
|
||||||
switch n := data.(type) {
|
|
||||||
case float64:
|
|
||||||
return n, true
|
|
||||||
case float32:
|
|
||||||
return float64(n), true
|
|
||||||
case int:
|
|
||||||
return float64(n), true
|
|
||||||
case int64:
|
|
||||||
return float64(n), true
|
|
||||||
case int32:
|
|
||||||
return float64(n), true
|
|
||||||
case int16:
|
|
||||||
return float64(n), true
|
|
||||||
case int8:
|
|
||||||
return float64(n), true
|
|
||||||
case uint:
|
|
||||||
return float64(n), true
|
|
||||||
case uint64:
|
|
||||||
return float64(n), true
|
|
||||||
case uint32:
|
|
||||||
return float64(n), true
|
|
||||||
case uint16:
|
|
||||||
return float64(n), true
|
|
||||||
case uint8:
|
|
||||||
return float64(n), true
|
|
||||||
default:
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BackupFormat) Marshal() (res string, err error) {
|
func (b *BackupFormat) Marshal() (res string, err error) {
|
||||||
data, err := marshalValue(reflect.ValueOf(b))
|
data, err := marshalValue(reflect.ValueOf(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -572,7 +286,13 @@ func (b *BackupFormat) Marshal() (res string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BackupFormat) Unmarshal(res string) (err error) {
|
func (b *BackupFormat) Unmarshal(res string) (err error) {
|
||||||
err = UnmarshalStruct([]byte(res), reflect.ValueOf(b))
|
// Parse the JSON data into a map
|
||||||
|
var jsonData interface{}
|
||||||
|
if err = json.Unmarshal([]byte(res), &jsonData); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the recursive unmarshaling process
|
||||||
|
err = unmarshalValueWithBackupTags(jsonData, reflect.ValueOf(b))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
287
services/project/backup_marshal.go
Normal file
287
services/project/backup_marshal.go
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
package project
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func marshalValue(v reflect.Value) (interface{}, error) {
|
||||||
|
// Handle pointers
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
if v.IsNil() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return marshalValue(v.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle structs
|
||||||
|
if v.Kind() == reflect.Struct {
|
||||||
|
typeOfV := v.Type()
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
fieldValue := v.Field(i)
|
||||||
|
fieldType := typeOfV.Field(i)
|
||||||
|
|
||||||
|
// Handle anonymous fields (embedded structs)
|
||||||
|
if fieldType.Anonymous {
|
||||||
|
embeddedValue, err := marshalValue(fieldValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if embeddedMap, ok := embeddedValue.(map[string]interface{}); ok {
|
||||||
|
// Merge embedded struct fields into parent result map
|
||||||
|
for k, v := range embeddedMap {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := fieldType.Tag.Get("backup")
|
||||||
|
|
||||||
|
// Check if the field should be backed up
|
||||||
|
if tag == "-" {
|
||||||
|
continue // Skip fields with backup:"-"
|
||||||
|
} else if tag == "" {
|
||||||
|
// Get the field name from the "db" tag
|
||||||
|
tag = fieldType.Tag.Get("db")
|
||||||
|
if tag == "" || tag == "-" {
|
||||||
|
continue // Skip if "db" tag is empty or "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively process the field value
|
||||||
|
value, err := marshalValue(fieldValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result[tag] = value
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle slices and arrays
|
||||||
|
if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
|
||||||
|
if v.IsNil() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var result []interface{}
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
elemValue, err := marshalValue(v.Index(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, elemValue)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle maps
|
||||||
|
if v.Kind() == reflect.Map {
|
||||||
|
if v.IsNil() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
for _, key := range v.MapKeys() {
|
||||||
|
// Assuming the key is a string
|
||||||
|
mapKey := fmt.Sprintf("%v", key.Interface())
|
||||||
|
mapValue, err := marshalValue(v.MapIndex(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result[mapKey] = mapValue
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle other types (int, string, etc.)
|
||||||
|
return v.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBasicType(data interface{}, v reflect.Value) error {
|
||||||
|
if !v.CanSet() {
|
||||||
|
return fmt.Errorf("cannot set value of type %v", v.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
b, ok := data.(bool)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected bool for field, got %T", data)
|
||||||
|
}
|
||||||
|
v.SetBool(b)
|
||||||
|
case reflect.String:
|
||||||
|
s, ok := data.(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected string for field, got %T", data)
|
||||||
|
}
|
||||||
|
v.SetString(s)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
n, ok := toFloat64(data)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected number for field, got %T", data)
|
||||||
|
}
|
||||||
|
v.SetInt(int64(n))
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
n, ok := toFloat64(data)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected number for field, got %T", data)
|
||||||
|
}
|
||||||
|
v.SetUint(uint64(n))
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
n, ok := toFloat64(data)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected number for field, got %T", data)
|
||||||
|
}
|
||||||
|
v.SetFloat(n)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported kind %v", v.Kind())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toFloat64(data interface{}) (float64, bool) {
|
||||||
|
switch n := data.(type) {
|
||||||
|
case float64:
|
||||||
|
return n, true
|
||||||
|
case float32:
|
||||||
|
return float64(n), true
|
||||||
|
case int:
|
||||||
|
return float64(n), true
|
||||||
|
case int64:
|
||||||
|
return float64(n), true
|
||||||
|
case int32:
|
||||||
|
return float64(n), true
|
||||||
|
case int16:
|
||||||
|
return float64(n), true
|
||||||
|
case int8:
|
||||||
|
return float64(n), true
|
||||||
|
case uint:
|
||||||
|
return float64(n), true
|
||||||
|
case uint64:
|
||||||
|
return float64(n), true
|
||||||
|
case uint32:
|
||||||
|
return float64(n), true
|
||||||
|
case uint16:
|
||||||
|
return float64(n), true
|
||||||
|
case uint8:
|
||||||
|
return float64(n), true
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalValueWithBackupTags(data interface{}, v reflect.Value) error {
|
||||||
|
// Handle pointers
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
// Initialize pointer if it's nil
|
||||||
|
if v.IsNil() {
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
return unmarshalValueWithBackupTags(data, v.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle structs
|
||||||
|
if v.Kind() == reflect.Struct {
|
||||||
|
// Data should be a map
|
||||||
|
m, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected object for struct, got %T", data)
|
||||||
|
}
|
||||||
|
return unmarshalStructWithBackupTags(m, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle slices and arrays
|
||||||
|
if v.Kind() == reflect.Slice {
|
||||||
|
dataSlice, ok := data.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected array for slice, got %T", data)
|
||||||
|
}
|
||||||
|
slice := reflect.MakeSlice(v.Type(), len(dataSlice), len(dataSlice))
|
||||||
|
for i := 0; i < len(dataSlice); i++ {
|
||||||
|
elem := slice.Index(i)
|
||||||
|
if err := unmarshalValueWithBackupTags(dataSlice[i], elem); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.Set(slice)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle maps
|
||||||
|
if v.Kind() == reflect.Map {
|
||||||
|
dataMap, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected object for map, got %T", data)
|
||||||
|
}
|
||||||
|
mapType := v.Type()
|
||||||
|
mapValue := reflect.MakeMap(mapType)
|
||||||
|
for key, value := range dataMap {
|
||||||
|
keyVal := reflect.ValueOf(key).Convert(mapType.Key())
|
||||||
|
valVal := reflect.New(mapType.Elem()).Elem()
|
||||||
|
if err := unmarshalValueWithBackupTags(value, valVal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mapValue.SetMapIndex(keyVal, valVal)
|
||||||
|
}
|
||||||
|
v.Set(mapValue)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle basic types
|
||||||
|
return setBasicType(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalStructWithBackupTags(data map[string]interface{}, v reflect.Value) error {
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
fieldType := t.Field(i)
|
||||||
|
fieldValue := v.Field(i)
|
||||||
|
|
||||||
|
// Handle anonymous fields (embedded structs)
|
||||||
|
if fieldType.Anonymous {
|
||||||
|
// Pass the entire data map to the embedded struct
|
||||||
|
if err := unmarshalStructWithBackupTags(data, fieldValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip fields with backup:"-"
|
||||||
|
if backupTag := fieldType.Tag.Get("backup"); backupTag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the JSON key to use
|
||||||
|
var jsonKey string
|
||||||
|
backupTag := fieldType.Tag.Get("backup")
|
||||||
|
if backupTag != "" {
|
||||||
|
jsonKey = backupTag
|
||||||
|
} else {
|
||||||
|
dbTag := fieldType.Tag.Get("db")
|
||||||
|
if dbTag != "" {
|
||||||
|
jsonKey = dbTag
|
||||||
|
} else {
|
||||||
|
continue // Skip if no backup or db tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the key exists in the data
|
||||||
|
if value, ok := data[jsonKey]; ok {
|
||||||
|
if !fieldValue.CanSet() {
|
||||||
|
continue // Skip fields that cannot be set
|
||||||
|
}
|
||||||
|
if value == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := unmarshalValueWithBackupTags(value, fieldValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user