github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/minver/minver.go (about) 1 // Copyright 2024 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package minver 16 17 import ( 18 "fmt" 19 "reflect" 20 "strings" 21 22 "gopkg.in/yaml.v2" 23 24 "github.com/dolthub/dolt/go/libraries/utils/version" 25 ) 26 27 func YamlForVersion(st any, versionNum uint32) ([]byte, error) { 28 err := NullUnsupported(versionNum, st) 29 if err != nil { 30 return nil, fmt.Errorf("error nulling unspported fields for version %d: %w", versionNum, err) 31 } 32 33 return yaml.Marshal(st) 34 } 35 36 func NullUnsupported(verNum uint32, st any) error { 37 const tagName = "minver" 38 39 // use reflection to loop over all fields in the struct st 40 // for each field check the tag "minver" and if the current version is less than that, set the field to nil 41 t := reflect.TypeOf(st) 42 43 if t.Kind() != reflect.Ptr { 44 return fmt.Errorf("expected a pointer to a struct, got %T", st) 45 } else if t.Kind() == reflect.Ptr { 46 t = t.Elem() 47 } 48 49 // Iterate over all available fields and read the tag value 50 for i := 0; i < t.NumField(); i++ { 51 // Get the field, returns https://golang.org/pkg/reflect/#StructField 52 field := t.Field(i) 53 54 // Get the field tag value 55 tag := field.Tag.Get(tagName) 56 57 if tag != "" { 58 // if it's nullable check to see if it should be set to nil 59 if field.Type.Kind() == reflect.Ptr || field.Type.Kind() == reflect.Slice || field.Type.Kind() == reflect.Map { 60 var setToNull bool 61 62 if tag == "TBD" { 63 setToNull = true 64 } else { 65 minver, err := version.Encode(tag) 66 if err != nil { 67 return fmt.Errorf("invalid version tag '%s' on field '%s': %w", tag, field.Name, err) 68 } 69 70 setToNull = verNum < minver 71 } 72 73 if setToNull { 74 // Get the field value 75 v := reflect.ValueOf(st).Elem().Field(i) 76 v.Set(reflect.Zero(v.Type())) 77 } 78 } else { 79 return fmt.Errorf("non-nullable field '%s' has a version tag '%s'", field.Name, tag) 80 } 81 82 var hasOmitEmpty bool 83 yamlTag := field.Tag.Get("yaml") 84 if yamlTag != "" { 85 vals := strings.Split(yamlTag, ",") 86 for _, val := range vals { 87 if val == "omitempty" { 88 hasOmitEmpty = true 89 break 90 } 91 } 92 } 93 94 if !hasOmitEmpty { 95 return fmt.Errorf("field '%s' has a version tag '%s' but no yaml tag with omitempty", field.Name, tag) 96 } 97 } 98 99 v := reflect.ValueOf(st).Elem().Field(i) 100 101 vIsNullable := v.Type().Kind() == reflect.Ptr || v.Type().Kind() == reflect.Slice || v.Type().Kind() == reflect.Map 102 103 if !vIsNullable || !v.IsNil() { 104 // if the field is a pointer to a struct, or a struct, or a slice recurse 105 if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct { 106 err := NullUnsupported(verNum, v.Interface()) 107 if err != nil { 108 return err 109 } 110 } else if field.Type.Kind() == reflect.Struct { 111 err := NullUnsupported(verNum, v.Addr().Interface()) 112 if err != nil { 113 return err 114 } 115 } else if field.Type.Kind() == reflect.Slice { 116 if field.Type.Elem().Kind() == reflect.Ptr && field.Type.Elem().Elem().Kind() == reflect.Struct { 117 for i := 0; i < v.Len(); i++ { 118 err := NullUnsupported(verNum, v.Index(i).Interface()) 119 if err != nil { 120 return err 121 } 122 } 123 } else if field.Type.Elem().Kind() == reflect.Struct { 124 for i := 0; i < v.Len(); i++ { 125 err := NullUnsupported(verNum, v.Index(i).Addr().Interface()) 126 if err != nil { 127 return err 128 } 129 } 130 } 131 } 132 } 133 } 134 135 return nil 136 }