package dto import ( "reflect" "golang.org/x/text/unicode/norm" ) // Normalize iterates through an object and performs Unicode normalization on all string fields with the `unorm` tag. func Normalize(obj any) { v := reflect.ValueOf(obj) if v.Kind() != reflect.Pointer || v.IsNil() { return } v = v.Elem() // Handle case where obj is a slice of models if v.Kind() == reflect.Slice { for i := 0; i < v.Len(); i++ { elem := v.Index(i) if elem.Kind() == reflect.Pointer && !elem.IsNil() && elem.Elem().Kind() == reflect.Struct { Normalize(elem.Interface()) } else if elem.Kind() == reflect.Struct && elem.CanAddr() { Normalize(elem.Addr().Interface()) } } return } if v.Kind() != reflect.Struct { return } // Iterate through all fields looking for those with the "unorm" tag t := v.Type() loop: for i := range t.NumField() { field := t.Field(i) unormTag := field.Tag.Get("unorm") if unormTag == "" { continue } fv := v.Field(i) if !fv.CanSet() || fv.Kind() != reflect.String { continue } var form norm.Form switch unormTag { case "nfc": form = norm.NFC case "nfkc": form = norm.NFKC case "nfd": form = norm.NFD case "nfkd": form = norm.NFKD default: continue loop } val := fv.String() val = form.String(val) fv.SetString(val) } }