mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-08-24 21:17:31 +00:00
86 lines
1.9 KiB
Go
86 lines
1.9 KiB
Go
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) {
|
|
normalizeValue(reflect.ValueOf(obj))
|
|
}
|
|
|
|
func normalizeValue(value reflect.Value) {
|
|
if !value.IsValid() {
|
|
return
|
|
}
|
|
|
|
// Unwrap interfaces and pointers so nested DTOs share the same traversal
|
|
kind := value.Kind()
|
|
if kind == reflect.Interface || kind == reflect.Pointer {
|
|
if !value.IsNil() {
|
|
normalizeValue(value.Elem())
|
|
}
|
|
return
|
|
}
|
|
|
|
// Walk collections because request DTOs may contain nested slices or arrays
|
|
if kind == reflect.Slice || kind == reflect.Array {
|
|
for i := range value.Len() {
|
|
normalizeValue(value.Index(i))
|
|
}
|
|
return
|
|
}
|
|
|
|
// Ignore scalar values because normalization is opt-in through struct field tags
|
|
if kind != reflect.Struct {
|
|
return
|
|
}
|
|
|
|
// Normalize tagged fields directly and recursively inspect untagged nested values
|
|
valueType := value.Type()
|
|
for i := range value.NumField() {
|
|
field := value.Field(i)
|
|
form, tagged := normalizationForm(valueType.Field(i).Tag.Get("unorm"))
|
|
if tagged {
|
|
normalizeString(field, form)
|
|
continue
|
|
}
|
|
|
|
normalizeValue(field)
|
|
}
|
|
}
|
|
|
|
func normalizationForm(tag string) (norm.Form, bool) {
|
|
switch tag {
|
|
case "nfc":
|
|
return norm.NFC, true
|
|
case "nfkc":
|
|
return norm.NFKC, true
|
|
case "nfd":
|
|
return norm.NFD, true
|
|
case "nfkd":
|
|
return norm.NFKD, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func normalizeString(value reflect.Value, form norm.Form) {
|
|
// Dereference optional string fields while leaving nil values unchanged
|
|
if value.Kind() == reflect.Pointer {
|
|
if value.IsNil() {
|
|
return
|
|
}
|
|
value = value.Elem()
|
|
}
|
|
|
|
// Ignore incompatible or read-only fields because reflection cannot safely update them
|
|
if value.Kind() != reflect.String || !value.CanSet() {
|
|
return
|
|
}
|
|
|
|
value.SetString(form.String(value.String()))
|
|
}
|