reflect.Type -> *structSpec
(t reflect.Type)
| 34 | var structSpecCache sync.Map // reflect.Type -> *structSpec |
| 35 | |
| 36 | func specFor(t reflect.Type) *structSpec { |
| 37 | if cached, ok := structSpecCache.Load(t); ok { |
| 38 | return cached.(*structSpec) |
| 39 | } |
| 40 | spec := &structSpec{byName: make(map[string]structFieldSpec, t.NumField())} |
| 41 | for i := range t.NumField() { |
| 42 | f := t.Field(i) |
| 43 | jsonName, _, _ := strings.Cut(f.Tag.Get("json"), ",") |
| 44 | if jsonName == "" || jsonName == "-" { |
| 45 | continue |
| 46 | } |
| 47 | fs := structFieldSpec{index: i, requiredID: -1} |
| 48 | var nullable bool |
| 49 | for marker := range strings.SplitSeq(f.Tag.Get("lsp"), ",") { |
| 50 | switch marker { |
| 51 | case "required": |
| 52 | fs.requiredID = len(spec.requiredNames) |
| 53 | spec.requiredMask |= 1 << fs.requiredID |
| 54 | spec.requiredNames = append(spec.requiredNames, jsonName) |
| 55 | case "nullable": |
| 56 | nullable = true |
| 57 | } |
| 58 | } |
| 59 | // A nilable field (pointer/slice/map) rejects an explicit JSON null |
| 60 | // unless the spec marks it nullable. |
| 61 | switch f.Type.Kind() { |
| 62 | case reflect.Pointer, reflect.Slice, reflect.Map: |
| 63 | fs.rejectNull = !nullable |
| 64 | } |
| 65 | spec.byName[jsonName] = fs |
| 66 | } |
| 67 | actual, _ := structSpecCache.LoadOrStore(t, spec) |
| 68 | return actual.(*structSpec) |
| 69 | } |
| 70 | |
| 71 | // unmarshalStruct decodes a JSON object into the struct pointed to by v, |
| 72 | // enforcing object-kind, required-field, and non-nullable-field strictness as |
no test coverage detected